如何将 Serde 与具有不同对象的 JSON 数组一起用于成功和错误?
How can I use Serde with a JSON array with different objects for successes and errors?
我想使用 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);
}
请注意,在反序列化中我们不能重复使用自动生成的反序列化器,因为:
- 反序列化是将字段流式传输给我们,我们无法 窥视 字符串化的 JSON 表示并猜测它是什么;
- 我们无权访问 Serde 生成的
serde::de::Visitor
实现。
我还做了一个快捷方式,panic
发现了错误。在生产代码中,您希望 return 正确的 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 代码中。
P.S。最近我倾向于在 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
}
如果性能不是问题,那么可以将动态部分反序列化为 Value.
同样,如果使用 Value
是一个选项,那么自定义反序列化可以通过实现 TryFrom<Value>
.
来简化
Serde 从版本 0.9.6 开始支持 internally tagged and untagged enums。
以下代码显示了如何通过使用具有属性 #[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 枚举表示的更多信息,请参见 overview。
我想使用 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);
}
请注意,在反序列化中我们不能重复使用自动生成的反序列化器,因为:
- 反序列化是将字段流式传输给我们,我们无法 窥视 字符串化的 JSON 表示并猜测它是什么;
- 我们无权访问 Serde 生成的
serde::de::Visitor
实现。
我还做了一个快捷方式,panic
发现了错误。在生产代码中,您希望 return 正确的 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 代码中。
P.S。最近我倾向于在 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
}
如果性能不是问题,那么可以将动态部分反序列化为 Value.
同样,如果使用 Value
是一个选项,那么自定义反序列化可以通过实现 TryFrom<Value>
.
Serde 从版本 0.9.6 开始支持 internally tagged and untagged enums。
以下代码显示了如何通过使用具有属性 #[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 枚举表示的更多信息,请参见 overview。