我想使用Serde创建一个包含错误消息和正确对象的数组:

extern crate serde; // 1.0.70
#[macro_use]
extern crate serde_derive; // 1.0.70
extern crate serde_json; // 1.0.24

#[derive(Serialize, Deserialize, Debug)]
pub struct MyError {
    error: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct MyAge {
    age: i32,
    name: String,
}

fn get_results(ages: Vec<i32>) -> Vec<MyAge> {
    let mut results = vec![];
    for age in ages {
        if age < 100 && age > 0 {
            results.push(MyAge {
                age: age,
                name: String::from("The dude"),
            });
        } else {
            results.push(MyError {
                error: String::from(format!("{} is invalid age", age)),
            });
        }
    }
    results
}

当我传入Vec [1, -6, 7]时,我想序列化为JSON:

[{"age": 1, "name": "The dude"},{"error": "-6 is invalid age"},{"age": 7, "name": "The dude"}]

我该怎么做?知道如何反序列化这样的数组也很好.

推荐答案

这里有一种方法:

#[macro_use]
extern crate serde_derive; // 1.0.117
extern crate serde; // 1.0.117
extern crate serde_json; // 1.0.59

#[derive(Serialize, Deserialize, Debug)]
pub struct MyError {
    error: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct MyAge {
    age: i32,
    name: String,
}

#[derive(Debug)]
enum AgeOrError {
    Age(MyAge),
    Error(MyError),
}

impl serde::Serialize for AgeOrError {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match self {
            &AgeOrError::Age(ref my_age) => serializer.serialize_some(my_age),
            &AgeOrError::Error(ref my_error) => serializer.serialize_some(my_error),
        }
    }
}

enum AgeOrErrorField {
    Age,
    Name,
    Error,
}

impl<'de> serde::Deserialize<'de> for AgeOrErrorField {
    fn deserialize<D>(deserializer: D) -> Result<AgeOrErrorField, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct AgeOrErrorFieldVisitor;

        impl<'de> serde::de::Visitor<'de> for AgeOrErrorFieldVisitor {
            type Value = AgeOrErrorField;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(formatter, "age or error")
            }

            fn visit_str<E>(self, value: &str) -> Result<AgeOrErrorField, E>
            where
                E: serde::de::Error,
            {
                Ok(match value {
                    "age" => AgeOrErrorField::Age,
                    "name" => AgeOrErrorField::Name,
                    "error" => AgeOrErrorField::Error,
                    _ => panic!("Unexpected field name: {}", value),
                })
            }
        }

        deserializer.deserialize_any(AgeOrErrorFieldVisitor)
    }
}

impl<'de> serde::Deserialize<'de> for AgeOrError {
    fn deserialize<D>(deserializer: D) -> Result<AgeOrError, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_map(AgeOrErrorVisitor)
    }
}

struct AgeOrErrorVisitor;

impl<'de> serde::de::Visitor<'de> for AgeOrErrorVisitor {
    type Value = AgeOrError;

    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(formatter, "age or error")
    }

    fn visit_map<A>(self, mut map: A) -> Result<AgeOrError, A::Error>
    where
        A: serde::de::MapAccess<'de>,
    {
        let mut age: Option<i32> = None;
        let mut name: Option<String> = None;
        let mut error: Option<String> = None;
        loop {
            match map.next_key()? {
                Some(AgeOrErrorField::Age) => age = map.next_value()?,
                Some(AgeOrErrorField::Name) => name = map.next_value()?,
                Some(AgeOrErrorField::Error) => error = map.next_value()?,
                None => break,
            }
        }
        if let Some(error) = error {
            Ok(AgeOrError::Error(MyError { error: error }))
        } else {
            Ok(AgeOrError::Age(MyAge {
                age: age.expect("!age"),
                name: name.expect("!name"),
            }))
        }
    }
}

fn get_results(ages: &[i32]) -> Vec<AgeOrError> {
    let mut results = Vec::with_capacity(ages.len());
    for &age in ages.iter() {
        if age < 100 && age > 0 {
            results.push(AgeOrError::Age(MyAge {
                age: age,
                name: String::from("The dude"),
            }));
        } else {
            results.push(AgeOrError::Error(MyError {
                error: format!("{} is invalid age", age),
            }));
        }
    }
    results
}

fn main() {
    let v = get_results(&[1, -6, 7]);
    let serialized = serde_json::to_string(&v).expect("Can't serialize");
    println!("serialized: {}", serialized);
    let deserialized: Vec<AgeOrError> =
        serde_json::from_str(&serialized).expect("Can't deserialize");
    println!("deserialized: {:?}", deserialized);
}

请注意,在反序列化中,我们不能重用自动生成的反序列化程序,因为:

  1. 反序列化是一种将字段流式传输给我们的过程,我们无法进入字符串化的JSON表示并猜测它是什么
  2. 我们无法访问Serde生成的serde::de::Visitor个实现.

我还抄了一条捷径,并查看了错误.在生产代码中,您希望返回正确的Serde错误.


另一个解决方案是使所有字段都是可选的合并 struct ,如下所示:

#[macro_use]
extern crate serde_derive; // 1.0.70
extern crate serde; // 1.0.70
extern crate serde_json; // 1.0.24

#[derive(Debug)]
pub struct MyError {
    error: String,
}

#[derive(Debug)]
pub struct MyAge {
    age: i32,
    name: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct MyAgeOrError {
    #[serde(skip_serializing_if = "Option::is_none")]
    age: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    error: Option<String>,
}

impl MyAgeOrError {
    fn from_age(age: MyAge) -> MyAgeOrError {
        MyAgeOrError {
            age: Some(age.age),
            name: Some(age.name),
            error: None,
        }
    }
    fn from_error(error: MyError) -> MyAgeOrError {
        MyAgeOrError {
            age: None,
            name: None,
            error: Some(error.error),
        }
    }
}

fn get_results(ages: &[i32]) -> Vec<MyAgeOrError> {
    let mut results = Vec::with_capacity(ages.len());
    for &age in ages.iter() {
        if age < 100 && age > 0 {
            results.push(MyAgeOrError::from_age(MyAge {
                age: age,
                name: String::from("The dude"),
            }));
        } else {
            results.push(MyAgeOrError::from_error(MyError {
                error: format!("{} is invalid age", age),
            }));
        }
    }
    results
}

fn main() {
    let v = get_results(&[1, -6, 7]);
    let serialized = serde_json::to_string(&v).expect("Can't serialize");
    println!("serialized: {}", serialized);
    let deserialized: Vec<MyAgeOrError> =
        serde_json::from_str(&serialized).expect("Can't deserialize");
    println!("deserialized: {:?}", deserialized);
}

我可以担保这一点,因为它允许Rust struct (例如MyAgeOrError)与JSON的布局相匹配.这样,JSON布局就会记录在Rust代码中.

另外,最近我倾向于在RawValue的帮助下延迟对可选或动态类型JSON部分的解码.不过,要将它们序列化很难,因为RawValue是一个借来的版本.例如,为了帮助实现序列化,可以将intern a RawValue提升到'static生命周期 :

use serde_json::value::{RawValue as RawJson};

fn intern_raw_json(raw_json: Box<RawJson>) -> &'static RawJson {
    use parking_lot::Mutex;
    use std::mem::transmute;

    static BUF: Mutex<Vec<Pin<Box<RawJson>>>> = Mutex::new(Vec::new());

    let buf = BUF.lock();
    let raw_json: Pin<Box<RawJson>> = raw_json.into();
    let pt: &'static RawJson = {
        let pt: &RawJson = &*raw_json;
        transmute(pt)
    };
    buf.push(raw_json);
    pt
}

If performance is not an issue, then one can deserialize the dynamic parts into the Value.
Similarly, if using Value is an option, then custom deserialization can be simplified by implementing TryFrom<Value>.

Rust相关问答推荐

如何从可变Pin中获取不可变Pin?

收集RangeInclusive T到Vec T<><>

告诉Rust编译器返回值不包含构造函数中提供的引用

铁 rust ,我的模块介绍突然遇到了一个问题

`*mut[T]`与`*mut T`的区别

应为关联类型,找到类型参数

如何使用reqwest进行异步请求?

为什么 tokio 在以奇怪的方式调用时只运行 n 个任务中的 n-1 个?

Cargo.toml:如何有条件地启用依赖项功能?

Rust Option 的空显式泛型参数

rust tokio::spawn 在 mutexguard 之后等待

Rust/Serde/HTTP:序列化`Option`

`移动||异步移动{...}`,如何知道哪个移动正在移动哪个?

Rust 将特性传递给依赖项

为什么1..=100返回一个范围而不是一个整数?

为什么在 macOS / iOS 上切换 WiFi 网络时 reqwest 响应会挂起?

如何在 Rust 中编写修改 struct 的函数

Rust,使用枚举从 HashMap 获取值

制作嵌套迭代器的迭代器

在 Rust 中枚举字符串的最佳方式? (字符()与 as_bytes())