我是为一个程序写的,这个程序连接到一个发送JSON的web服务.

当某个属性不存在时,它会提供一个空对象,其所有字段都是空字符串,而不是排除值.当属性存在时,某些属性为u64.我怎么能让Serde处理这个案子?

rust struct

#[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"

Rust相关问答推荐

错误[E0793]具体何时发生:对打包字段的引用未对齐触发?

为什么Tauri要修改被调用函数的参数名称?

rust 迹-内存管理-POP所有权-链表

如何编写一个以一个闭包为参数的函数,该函数以另一个闭包为参数?

替换可变引用中的字符串会泄漏内存吗?

有没有一种惯用的方法来判断VEC中是否存在变体?

JSON5中的变量类型(serde)

允许 rust 迹 struct 条目具有多种类型

正在将带有盒的异步特征迁移到新的异步_fn_in_特征功能

为什么比较Option<;字符串>;具有常数Option<&;str>;需要显式类型转换吗?

通过异常从同步代码中产生yield 是如何工作的?

使用 serde::from_value 反序列化为泛型类型

从 rust 函数返回 &HashMap

在 Rust 中查找向量中 dyn struct 的索引

从 HashMap>, _> 中删除的生命周期问题

如何判断服务器是否正确接收数据

如何在 Rust 中将 bson::Bson 转换为 Vec

隐式类型闭包的错误生命周期推断

TinyVec 如何与 Vec 大小相同?

当值是新类型包装器时,对键的奇怪 HashMap 生命周期要求