如何正确处理 empty、null 和 valid JSON?

How to properly handle empty, null and valid JSON?

我需要在 Rust 中将 JSON 文件反序列化为 NoneSome(T)。当没有值时,我们使用的来源将提供 null 或空的“{}”,JSON 字段。我想将两者都作为 None 情况处理,并且仅在 JSON 字段不为空或为空时才反序列化。

input: {"test": null} -> output: {"test": None}
input: {"test": {}} -> output: {"test": None}
input: {"test": {"valid_json": 42}} -> output: {"test": {"valid_json": 42}}

我能找到的所有答案都解决了一个或另一个问题,但不能同时解决这两个问题。

use serde::{Deserialize, Deserializer};

#[derive(Deserialize, Debug, PartialEq)]
struct Foo {
    #[serde(deserialize_with = "object_empty_as_none")]
    bar: Option<Bar>,
}

#[derive(Deserialize, Debug, PartialEq)]
struct Bar {
    inner: u32,
}

pub fn object_empty_as_none<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
    D: Deserializer<'de>,
    for<'a> T: Deserialize<'a>,
{
    #[derive(Deserialize, Debug)]
    #[serde(deny_unknown_fields)]
    struct Empty {}

    #[derive(Deserialize, Debug)]
    #[serde(untagged)]
    enum Aux<T> {
        T(T),
        Empty(Empty),
        Null,
    }

    match Deserialize::deserialize(deserializer)? {
        Aux::T(t) => Ok(Some(t)),
        Aux::Empty(_) | Aux::Null => Ok(None),
    }
}

fn main() {
    let data = r#"{"bar": null}"#;
    let v: Foo = serde_json::from_str(data).unwrap();
    assert_eq!(v, Foo { bar: None });

    let data = r#"{"bar": {}}"#;
    let v: Foo = serde_json::from_str(data).unwrap();
    assert_eq!(v, Foo { bar: None });

    let data = r#"{"bar": {"inner": 42}}"#;
    let v: Foo = serde_json::from_str(data).unwrap();
    assert_eq!(
        v,
        Foo {
            bar: Some(Bar { inner: 42 })
        }
    );

    let data = r#"{"bar": {"not_inner": 42}}"#;
    let v: Result<Foo, _> = serde_json::from_str(data);
    assert!(v.is_err());
}

大多数情况下应该足够了。如果需要,请删除 Empty 上的 #[serde(deny_unknown_fields)]

This page tells you how to implement a custom map deserializer, which requires customizing how visit_map produces key-value pairs from the input data. I've basically copied that page and produced a minimal example that implements what you're looking for. Link to playground.

use std::fmt;
use std::marker::PhantomData;

use serde::de::{Deserialize, Deserializer, MapAccess, Visitor};
use serde_json::Value as JsonValue;
use std::collections::HashMap;

#[derive(Debug)]
struct MyMap(HashMap<String, JsonValue>);

impl MyMap {
    fn with_capacity(capacity: usize) -> Self {
        Self(HashMap::with_capacity(capacity))
    }
}

struct MyMapVisitor {
    marker: PhantomData<fn() -> MyMap>,
}

impl MyMapVisitor {
    fn new() -> Self {
        MyMapVisitor {
            marker: PhantomData,
        }
    }
}

impl<'de> Visitor<'de> for MyMapVisitor {
    // The type that our Visitor is going to produce.
    type Value = MyMap;

    // Format a message stating what data this Visitor expects to receive.
    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a very special map")
    }

    // Deserialize MyMap from an abstract "map" provided by the
    // Deserializer. The MapAccess input is a callback provided by
    // the Deserializer to let us see each entry in the map.
    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut map = MyMap::with_capacity(access.size_hint().unwrap_or(0));

        // While there are entries remaining in the input, add them
        // into our map. Empty Objects get turned into Null.
        while let Some((key, value)) = access.next_entry()? {
            let value = match value {
                JsonValue::Object(o) if o.is_empty() => JsonValue::Null,
                _ => value,
            };
            map.0.insert(key, value);
        }

        Ok(map)
    }
}

// This is the trait that informs Serde how to deserialize MyMap.
impl<'de> Deserialize<'de> for MyMap {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // Instantiate our Visitor and ask the Deserializer to drive
        // it over the input data, resulting in an instance of MyMap.
        deserializer.deserialize_map(MyMapVisitor::new())
    }
}

fn main() -> serde_json::Result<()> {
    let json_str = r#"{"a": null, "b": {}, "c": {"valid_json": 42}}"#;
    let v: MyMap = serde_json::from_str(json_str)?;
    println!("{:?}", v);
    Ok(())
}

这会打印出 MyMap({"b": Null, "c": Object({"valid_json": Number(42)}), "a": Null}),我相信这就是您想要的。