json - 使用 Serde 将两种类型转换为一种类型

标签 json rust deserialization serde

我正在编写一个程序,该程序挂接到发送回 JSON 的 Web 服务。

当某个属性不存在时,它会提供一个空对象,其所有字段都是空字符串,而不是排除值。当属性存在时,部分属性为u64。我怎样才能让 Serde 处理这种情况?

Rust 结构

#[derive(Clone, Debug, Deserialize)]
struct WebResponse {
    foo: Vec<Foo>,
}

#[derive(Clone, Debug, Deserialize)]
struct Foo {
    points: Points,
}

#[derive(Clone, Debug, Deserialize)]
struct Points {
    x: u64,
    y: u64,
    name: String,
}

示例 JSON

{
    "foo":[
        {
            "points":{
                "x":"",
                "y":"",
                "name":""
            }
        },
        {
            "points":{
                "x":78,
                "y":92,
                "name":"bar"
            }
        }
    ]
}

最佳答案

Serde 支持一个有趣的选择 attributes可用于自定义类型的序列化或反序列化,同时大部分仍使用派生实现。

在您的情况下,您需要能够解码可以指定为多种类型之一的字段,并且不需要来自其他字段的信息来决定如何解码有问题的字段。 #[serde(deserialize_with="$path")] 注释非常适合解决您的问题。

我们需要定义一个函数,将空字符串或整数值解码为 u64。我们可以对两个字段使用相同的函数,因为我们需要相同的行为。此函数将使用自定义 Visitor能够处理字符串和整数。有点长,但它让您感谢Serde为您所做的所有工作!

extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use serde::Deserializer;
use serde::de::{self, Unexpected};
use std::fmt;

#[derive(Clone, Debug, Deserialize)]
struct WebResponse {
    foo: Vec<Foo>,
}

#[derive(Clone, Debug, Deserialize)]
struct Foo {
    points: Points,
}

#[derive(Clone, Debug, Deserialize)]
struct Points {
    #[serde(deserialize_with = "deserialize_u64_or_empty_string")]
    x: u64,
    #[serde(deserialize_with = "deserialize_u64_or_empty_string")]
    y: u64,
    name: String,
}

struct DeserializeU64OrEmptyStringVisitor;

impl<'de> de::Visitor<'de> for DeserializeU64OrEmptyStringVisitor {
    type Value = u64;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("an integer or a string")
    }

    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(v)
    }

    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        if v == "" {
            Ok(0)
        } else {
            Err(E::invalid_value(Unexpected::Str(v), &self))
        }
    }
}

fn deserialize_u64_or_empty_string<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
    D: Deserializer<'de>,
{
    deserializer.deserialize_any(DeserializeU64OrEmptyStringVisitor)
}

fn main() {
    let value = serde_json::from_str::<WebResponse>(
        r#"{
        "foo": [
            {
                "points": {
                    "x": "",
                    "y": "",
                    "name": ""
                }
            },
            {
                "points": {
                    "x": 78,
                    "y": 92,
                    "name": "bar"
                }
            }
        ]
    }"#,
    );
    println!("{:?}", value);
}

Cargo.toml:

[dependencies]
serde = "1.0.15"
serde_json = "1.0.4"
serde_derive = "1.0.15"

关于json - 使用 Serde 将两种类型转换为一种类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37870428/

相关文章:

rust - Rust 中 f32 的 frexp 函数在哪里?

overloading - 我怎样才能近似方法重载?

java - JSON 反序列化时的 Base64 解码值

Php json_encode 在 2 个不同的服务器上为同一对象返回不同的值

python - 在反序列化过程中,是否有任何方便的方法可以将 python 中的 json 键从 string_int 转换为 int

html - 如何创建 <tr><td> 宏?

java - JSON 序列化对象的日期格式为 2006-10-04T19 :49:49. 无法对其进行反序列化

java - 从 XML 消息中查找模式,从 XML 生成类并在 JAVA 中反序列化

java - Java中JSON格式之间的映射

javascript - 将 JSON 从 PHP Controller 传递到 AngularJS Controller 的最佳方法?