Ok so straight to the point, i’m working with some broken json data.
{
data: "stringified json here"
}
one of the fields is a stringified json. i need to tell serde to deserialize the field as a struct and not a string.
the docs say i can achieve this with the #[serde(deserialize_with)]
attribute, but i’m ashamed to say i can’t figure it out.
#[derive(Serialize, Deserialize)]
struct Data {
#[serde(deserialize_with = "deserialize_data")]
data: ActualData
}
fn deserialize_data<'de, D>(data: D) -> Result<ActualData, D::Error>
where
D: Deserializer<'de>,
{
// i have no idea how i transform data: String to ActualData
}
1 Like
@seunlanlege In the body of deserialize_data
you could use serde_json
to deserialize the data within the stringified field. The Deserialize
API can be tricky to hold, but following the docs on implementing Deserialize
manually, you could do something like this:
#[derive(Debug, Deserialize)]
struct Data {
#[serde(deserialize_with = "deserialize_json_string")]
data: ActualData,
}
#[derive(Debug, Deserialize)]
struct ActualData {
a: i32,
b: String,
}
fn deserialize_json_string<'de, D>(deserializer: D) -> Result<ActualData, D::Error>
where
D: de::Deserializer<'de>,
{
// define a visitor that deserializes
// `ActualData` encoded as json within a string
struct JsonStringVisitor;
impl<'de> de::Visitor<'de> for JsonStringVisitor {
type Value = ActualData;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string containing json data")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
// unfortunately we lose some typed information
// from errors deserializing the json string
serde_json::from_str(v).map_err(E::custom)
}
}
// use our visitor to deserialize an `ActualValue`
deserializer.deserialize_any(JsonStringVisitor)
}
Here’s a runnable example.
3 Likes
No problem!
I always keep the docs for serde
close by for cases like this where you need to work with Serializer
s or Deserializer
s directly. Real data can be real messy.
1 Like
Sorry for necroposting, but googling serde deserialize_with
leads here so I thouht I'd add a tip for anyone who might stumble upon this topic in the future:
You can of course implement the Visitor
trait as described above, but IMO in many cases it is simpler to use Deserialize
implementation on existing types, like this:
fn deserialize_json_string<'de, D>(deserializer: D) -> Result<ActualData, D::Error>
where
D: de::Deserializer<'de>,
{
let s: &str = de::Deserialize::deserialize(deserializer)?;
serde_json::from_str(s).map_err(de::Error::custom)
}
24 Likes