我该如何在包含成功和错误对象的JSON数组中使用Serde?

11

我希望使用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"}]

我该怎么做?如果知道如何反序列化这样的数组,那就太好了。


1
请显示当前序列化结果。 - oli_obk
1
@ker 它不能序列化为任何东西。这不是有效的代码,因为"MyError"和"MyAge"不是兼容类型。 - user3384741
1
哦哦哦...你尝试过使用枚举吗?解决问题的方法有很多种。这些解决方案需要编写不同数量和类型的代码...你必须遵守确切的输出格式吗? - oli_obk
2个回答

14

从0.9.6版本开始,Serde支持内部标记未标记的枚举

以下代码展示了如何使用带有属性#[serde(untagged)]的枚举来实现此功能的示例。

#[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,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum AgeOrError {
    Age(MyAge),
    Error(MyError),
}

fn get_results(ages: Vec<i32>) -> Vec<AgeOrError> {
    let mut results = Vec::with_capacity(ages.len());
    for age in ages {
        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 results = get_results(vec![1, -6, 7]);
    let json = serde_json::to_string(&results).unwrap();
    println!("{}", json);
}

上述代码输出以下JSON:

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

有关Serde枚举表示的更多信息,请参阅概述


13
这是一种实现方式:

以下是具体步骤:

#[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 实现。
此外,我在处理错误时采取了一种捷径并使用 panic。在生产代码中,您需要返回适当的 Serde 错误。
另一种解决方案是创建一个包含所有字段可选的合并结构,如下所示:
#[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结构(例如MyAgeOrError)匹配您的JSON布局。这样,JSON布局就在Rust代码中得到了记录。
另外,最近我倾向于使用RawValue来延迟解码可选或动态类型的JSON部分。但是,由于RawValue是一个借用,所以对它们进行序列化很棘手。例如,为了帮助序列化,可以将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
}

如果性能不是问题,那么可以将动态部分反序列化为 Value
同样,如果使用 Value 是一个选择,那么可以通过实现 TryFrom<Value> 来简化自定义反序列化。

2
Rust 再次将简单的事情变得困难了。 - nikoss
1
@nikoss,Serde旨在快速且通用于多种编码格式。据我所知,它是最快的JSON库之一。(上次我检查时,只有不安全的C++ rapidjson更快)。一定程度的静态类型是必要的权衡,它允许我们直接解码到结构中,避免了临时堆分配。如果您不需要额外的速度和类型化结构的便利性,可以通过使用动态类型的JSON库(或通过serde_json::Value使用Serde的动态类型部分)来保持简单。 - ArtemGr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接