json
json copied to clipboard
Isolate fields that exist in the JSON value but were not present in the struct deserialized into
What's the cleanest way to handle a really scary problem I'm sure everyone is aware of:
- You create a struct to deserialize all fields that you think exist in a JSON value
- Unbeknownst to you, the producer of that JSON value adds new fields to the JSON value that your struct does not account for
- Your code silently fails by ignoring those fields since they aren't present in the struct you're deserializing into.
So how could we use serde_json to obtain a Value containing only the fields that were not present in the struct? It's sort of like a diff showing everything your struct is not accounting for.
My end goal would be to add a field to my struct that is just a String value containing a stringified representation of all fields not accounted for by my struct so that at least as a stop gap I'm not discarding them.
You can use #[serde(flatten)] to gather unknown fields into a Value or BtreeMap. Another option is to let the deserialization fail with #[serde(deny_unknown_fields)] whenever unknown field occur, then you can update the struct.
#![allow(unused_must_use, dead_code)]
use std::collections::BTreeMap;
use serde::*;
fn main() {
let json = r#"{
"a": 1,
"b": 2,
"extra": false,
"extra2": ["Hi"]
}"#;
#[derive(Debug, Deserialize)]
struct A {
a: i32,
b: u32,
#[serde(flatten)]
extra: serde_json::Value,
}
dbg!(serde_json::from_str::<A>(json));
#[derive(Debug, Deserialize)]
struct B {
a: i32,
b: u32,
#[serde(flatten)]
extra: BTreeMap<String, serde_json::Value>,
}
dbg!(serde_json::from_str::<B>(json));
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct C {
a: i32,
b: u32,
}
dbg!(serde_json::from_str::<C>(json));
}
[src/main.rs:22:5] serde_json::from_str::<A>(json) = Ok(
A {
a: 1,
b: 2,
extra: Object {
"extra": Bool(false),
"extra2": Array [
String("Hi"),
],
},
},
)
[src/main.rs:32:5] serde_json::from_str::<B>(json) = Ok(
B {
a: 1,
b: 2,
extra: {
"extra": Bool(false),
"extra2": Array [
String("Hi"),
],
},
},
)
[src/main.rs:41:5] serde_json::from_str::<C>(json) = Err(
Error("unknown field `extra`, expected `a` or `b`", line: 4, column: 15),
)
Truly a beautiful and succinct API. Thank you!