Serde: Treat null and missing field as being different

Created on 10 Jul 2017  路  5Comments  路  Source: serde-rs/serde

From IRC:

hmm, anyone know a way to treat serde_json::Value::Null and a field not existing differently without a custom deserialize?
ideally I'd have a type Option, where Some(None) means a null value, and None means it didn't exist at all

support

Most helpful comment

This is one possible approach:

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use serde::{Deserialize, Deserializer};

#[derive(Deserialize, Debug)]
struct S {
    #[serde(default, deserialize_with = "deserialize_some")]
    a: Option<Option<i32>>,
}

// Any value that is present is considered Some value, including null.
fn deserialize_some<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
    where T: Deserialize<'de>,
          D: Deserializer<'de>
{
    Deserialize::deserialize(deserializer).map(Some)
}

fn main() {
    println!("{:?}", serde_json::from_str::<S>("{}").unwrap());
    println!("{:?}", serde_json::from_str::<S>("{\"a\":null}").unwrap());
    println!("{:?}", serde_json::from_str::<S>("{\"a\":1}").unwrap());
}

All 5 comments

This is one possible approach:

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use serde::{Deserialize, Deserializer};

#[derive(Deserialize, Debug)]
struct S {
    #[serde(default, deserialize_with = "deserialize_some")]
    a: Option<Option<i32>>,
}

// Any value that is present is considered Some value, including null.
fn deserialize_some<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
    where T: Deserialize<'de>,
          D: Deserializer<'de>
{
    Deserialize::deserialize(deserializer).map(Some)
}

fn main() {
    println!("{:?}", serde_json::from_str::<S>("{}").unwrap());
    println!("{:?}", serde_json::from_str::<S>("{\"a\":null}").unwrap());
    println!("{:?}", serde_json::from_str::<S>("{\"a\":1}").unwrap());
}

That's a pretty awesome approach! Thank you, I will be using that.

The context for this is a server which sends "update" json messages - a field missing from the message means that nothing has changed, but a null value means the old data should be removed.

That custom method might actually be more useful than any built-in option for Option<Option<>> for me even, because (I think) it'll work even for a plain Option<InnerType> fields. I'm using a macro_rules!() macro to create the update struct to go along with the regular struct, so applying it to everything would make it much simplier.

I used this in the https://github.com/vitiral/jrpc crate! Thanks!

Is there a way to apply deserialize_with to all fields of a struct? It is kinda tedious to write that annotation above every field in my struct when i want every field to behave as explained above.

@cars10 I think you could wrap this into a new type and then implement Serialize/Deserialize for that type?

Was this page helpful?
0 / 5 - 0 ratings