有没有办法用 Serde 定义标签字段?
Is there a way to define a tag field with Serde?
我想要这样的东西:
#[derive(Debug, Serialize, Deserialize)]
struct MyStruct {
field1: String,
field2: Option<u64>,
#[serde(tag(value = "tag_value"))]
tag: ()
}
#[serde(tag(value = "tag_value"))]
并不是 Serde 实际提供的属性,它只是表达一个想法。我知道我可以自己完成所有序列化,使用远程、(de
)serialize_with
等,但是这些需要大量样板代码。
思路是必须存在一个tag字段,MyStruct
序列化为JSON应该是:
{
"field1": "foo",
"field2": 42,
"tag": "tag_value"
}
如果 "tag" 字段缺失或映射到与 "tag_value" 不同的值,反序列化必须失败。
使用单个变体枚举:
use serde; // 1.0.104
use serde_json; // 1.0.48
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
enum Tag {
TagValue,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct MyStruct {
field1: String,
field2: Option<u64>,
tag: Tag,
}
fn main() {
let s = MyStruct {
field1: "foo".to_string(),
field2: Some(42),
tag: Tag::TagValue,
};
// The tag is included when serializing
println!("{:?}", serde_json::to_string(&s));
// Tag is required when deserializing
println!(
"{:?}",
serde_json::from_str::<MyStruct>(
"{\"field1\":\"foo\",\"field2\":42,\"tag\":\"tag_value\"}"
)
);
println!(
"{:?}",
serde_json::from_str::<MyStruct>("{\"field1\":\"foo\",\"field2\":42}")
);
// A bad tag fails
println!(
"{:?}",
serde_json::from_str::<MyStruct>("{\"field1\":\"foo\",\"field2\":42,\"tag\":\"oops\"}")
);
}
这会打印
Ok("{\"field1\":\"foo\",\"field2\":42,\"tag\":\"tag_value\"}")
Ok(MyStruct { field1: "foo", field2: Some(42), tag: TagValue })
Err(Error("missing field `tag`", line: 1, column: 28))
Err(Error("unknown variant `oops`, expected `tag_value`", line: 1, column: 40))
对于只有一个标签的有限情况,您可以在结构本身上使用 #[serde(tag)]
:
use serde::Serialize; // 1.0.114
use serde_json; // 1.0.56
#[derive(Debug, Serialize)]
#[serde(tag = "tag", rename = "tag_value")]
struct MyStruct {
field1: String,
field2: Option<u64>,
}
fn main() {
let s = MyStruct {
field1: "hello".into(),
field2: None,
};
println!("{}", serde_json::to_string(&s).unwrap());
}
{"tag":"tag_value","field1":"hello","field2":null}
我想要这样的东西:
#[derive(Debug, Serialize, Deserialize)]
struct MyStruct {
field1: String,
field2: Option<u64>,
#[serde(tag(value = "tag_value"))]
tag: ()
}
#[serde(tag(value = "tag_value"))]
并不是 Serde 实际提供的属性,它只是表达一个想法。我知道我可以自己完成所有序列化,使用远程、(de
)serialize_with
等,但是这些需要大量样板代码。
思路是必须存在一个tag字段,MyStruct
序列化为JSON应该是:
{
"field1": "foo",
"field2": 42,
"tag": "tag_value"
}
如果 "tag" 字段缺失或映射到与 "tag_value" 不同的值,反序列化必须失败。
使用单个变体枚举:
use serde; // 1.0.104
use serde_json; // 1.0.48
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
enum Tag {
TagValue,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct MyStruct {
field1: String,
field2: Option<u64>,
tag: Tag,
}
fn main() {
let s = MyStruct {
field1: "foo".to_string(),
field2: Some(42),
tag: Tag::TagValue,
};
// The tag is included when serializing
println!("{:?}", serde_json::to_string(&s));
// Tag is required when deserializing
println!(
"{:?}",
serde_json::from_str::<MyStruct>(
"{\"field1\":\"foo\",\"field2\":42,\"tag\":\"tag_value\"}"
)
);
println!(
"{:?}",
serde_json::from_str::<MyStruct>("{\"field1\":\"foo\",\"field2\":42}")
);
// A bad tag fails
println!(
"{:?}",
serde_json::from_str::<MyStruct>("{\"field1\":\"foo\",\"field2\":42,\"tag\":\"oops\"}")
);
}
这会打印
Ok("{\"field1\":\"foo\",\"field2\":42,\"tag\":\"tag_value\"}")
Ok(MyStruct { field1: "foo", field2: Some(42), tag: TagValue })
Err(Error("missing field `tag`", line: 1, column: 28))
Err(Error("unknown variant `oops`, expected `tag_value`", line: 1, column: 40))
对于只有一个标签的有限情况,您可以在结构本身上使用 #[serde(tag)]
:
use serde::Serialize; // 1.0.114
use serde_json; // 1.0.56
#[derive(Debug, Serialize)]
#[serde(tag = "tag", rename = "tag_value")]
struct MyStruct {
field1: String,
field2: Option<u64>,
}
fn main() {
let s = MyStruct {
field1: "hello".into(),
field2: None,
};
println!("{}", serde_json::to_string(&s).unwrap());
}
{"tag":"tag_value","field1":"hello","field2":null}