Serde: Deserialize nested JSON string

Created on 21 Jul 2017  路  4Comments  路  Source: serde-rs/serde

Question from IRC:

\ serde_json doesn't seem to be able to deserialize valid json with backslashes
\ {"sms":"{\"Source\":4477665544,\"Destination\":1231231,\"Content\":\"Hello from SMPPSim\"}","uuid":"69e123f4-4ced-4f8f-9853-df20ebc3937b"}
\ is no go
\ i'm guessing you mean the inner json there
\ ?
\ yep
\ let me show you my code
\ yeah, you'd have to unescape it first
\ https://play.rust-lang.org/?gist=3fb18ca55def35c626508445c49606dc&version=stable
\ How do I unescape it without touching the backslashes that might be inside the field's value?
\ i don't know to be honest
\ i've never seen JSON embedded inside of a string in JSON
\ Go seems to handle such deserialization just fine
\ gitstash: you'd probably want to first deserialize to a `struct Foo { sms: String }` and then reparse the sms field
\ nested json seems pretty weird
\ sfackler: That's messy
\ gitstash: so is stringifying json and sticking it in json :P
\ ill do as you say then
\ thank you

@gitstash @steveklabnik @sfackler

support

Most helpful comment

In any case, here it is in Rust (with both serialize and deserialize).

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

#[derive(Serialize, Deserialize, Debug)]
struct Message {
    #[serde(with = "json_string")]
    sms: Sms,
    uuid: String,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct Sms {
    source: u64,
    destination: u64,
    content: String,
}

mod json_string {
    use serde_json;
    use serde::ser::{self, Serialize, Serializer};
    use serde::de::{self, Deserialize, DeserializeOwned, Deserializer};

    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
        where T: Serialize,
              S: Serializer
    {
        let j = serde_json::to_string(value).map_err(ser::Error::custom)?;
        j.serialize(serializer)
    }

    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
        where T: DeserializeOwned,
              D: Deserializer<'de>
    {
        let j = String::deserialize(deserializer)?;
        serde_json::from_str(&j).map_err(de::Error::custom)
    }
}

fn main() {
    let msg = r#"
      {
        "sms":"{\"Source\":4477665544,\"Destination\":1231231,\"Content\":\"Hello from SMPPSim\"}",
        "uuid":"69e123f4-4ced-4f8f-9853-df20ebc3937b"
      }"#;

    let de = serde_json::from_str::<Message>(msg).unwrap();
    println!("{:#?}", de);

    let ser = serde_json::to_string_pretty(&de).unwrap();
    println!("{}", ser);
}

All 4 comments

\ Go seems to handle such deserialization just fine

I was not able to reproduce this.

package main

import (
    "fmt"
    "encoding/json"
)

type Message struct {
    SMS SMS
    UUID string
}

type SMS struct {
    Source uint64
    Destination uint64
    Content string
}

func main() {
    msg := `{
        "sms":"{\"Source\":4477665544,\"Destination\":1231231,\"Content\":\"Hello from SMPPSim\"}",
        "uuid":"69e123f4-4ced-4f8f-9853-df20ebc3937b"
    }`

    var de Message
    err := json.Unmarshal([]byte(msg), &de)
    if err != nil {
        fmt.Println(err)
    }
}
json: cannot unmarshal string into Go struct field Message.SMS of type main.SMS

In any case, here it is in Rust (with both serialize and deserialize).

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

#[derive(Serialize, Deserialize, Debug)]
struct Message {
    #[serde(with = "json_string")]
    sms: Sms,
    uuid: String,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct Sms {
    source: u64,
    destination: u64,
    content: String,
}

mod json_string {
    use serde_json;
    use serde::ser::{self, Serialize, Serializer};
    use serde::de::{self, Deserialize, DeserializeOwned, Deserializer};

    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
        where T: Serialize,
              S: Serializer
    {
        let j = serde_json::to_string(value).map_err(ser::Error::custom)?;
        j.serialize(serializer)
    }

    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
        where T: DeserializeOwned,
              D: Deserializer<'de>
    {
        let j = String::deserialize(deserializer)?;
        serde_json::from_str(&j).map_err(de::Error::custom)
    }
}

fn main() {
    let msg = r#"
      {
        "sms":"{\"Source\":4477665544,\"Destination\":1231231,\"Content\":\"Hello from SMPPSim\"}",
        "uuid":"69e123f4-4ced-4f8f-9853-df20ebc3937b"
      }"#;

    let de = serde_json::from_str::<Message>(msg).unwrap();
    println!("{:#?}", de);

    let ser = serde_json::to_string_pretty(&de).unwrap();
    println!("{}", ser);
}

A candidate for attribute or a public module in serde_json so we can write #[serde(with = "serde_json::nested")]?

I added it to the list in #553.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vityafx picture vityafx  路  3Comments

swfsql picture swfsql  路  3Comments

sackery picture sackery  路  3Comments

dtolnay picture dtolnay  路  3Comments

Boscop picture Boscop  路  4Comments