RUST:多态结构JSON对象的反序列化


嗯,Rust初学者,在各种坑中摔的鼻青脸肿。
一个简单的“服务端JSON转为CSV”的小程序,如果是常用的Java、Ruby,甚至是Shell,一个下午的时间也差不多了,偏偏脑子一抽用了Rust,硬是折腾了快10天了。即使这样也不敢保证程序的强壮性。
这里记录一下,算是一篇Rust的学习笔记吧。
先描述一下我要解决的问题:
服务端返回的JSON结构有多种形态:
[
"id": 1,
"name": "..."
"id": 2,
"name": "...",
"value": "..."
]
很简单的,一个结构体搞定:
#[derive(Clone, Debug, serde::Deserialize)]
struct CustomValue {
id: u32,
name: String,
value: Option<String>,
借助
serde
,开发过程中这一步很容易就实现了,可发布之后却立马打脸。原来这个结构还有第三个形态。
{
"id": 3,
"name": "...",
"multiple": true,
"value": ["...", "..."]
}
中间试错的过程就不说了,直接给出解决方案: 用枚举替代结构体 。
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
enum CustomValue {
Multiple {
id: u32,
name: String,
value: Vec<String>,
Single {
id: u32,
name: String,
value: String,
Empty {
id: u32,
name: String,
#[test]
pub fn test_field_deserialize() -> serde_json::Result<()> {
let a = r#"{"id": 1, "name": "empty"}"#;
let b = r#"{"id": 2, "name": "single", "value": "string"}"#;
let c = r#"{"id": 3, "name": "multiple", "multiple": true, "value": ["first", "second"]}"#;
let mut v: CustomValue = serde_json::from_str(a)?;
match v {
CustomValue::Empty { id, name } => {
assert_eq!(id, 1);
assert_eq!(name, "empty");
_ => {
panic!("expect Empty");
v = serde_json::from_str(b)?;
match v {
CustomValue::Single { id, name, value } => {
assert_eq!(id, 2);
assert_eq!(name, "single");
assert_eq!(value, "string")
_ => {
panic!("expect Single");
v = serde_json::from_str(c)?;
match v {
CustomValue::Multiple { id, name, value } => {
let mut data = Vec::<String>::new();
data.push("first".to_string());
data.push("second".to_string());
assert_eq!(id, 3);
assert_eq!(name, "multiple");
assert_eq!(value, data);
_ => {
panic!("expect Multiple");
Ok(())
重点是:
-
使用
untagged
给枚举做注解 -
枚举值的顺序要注意。
serde
的做法是依次偿试,遇到的第一个能匹配的就做为结果返回。如果最先定义Empty
,那么所有的形态都能与其匹配,那么就达不到我们的本意了。
说起来也是可恨,这个问题的对策,在
serde
的官方文档中就有示范(
Enum representations
)。可恨是的公司代理服务器一到用时就掉链子,访问外部网站简直想让人砸电脑。如果你让我看一下这个网页,十几分钟就能解决掉的问题,硬是花了我一天的时间没解决,最后是回到家里查看了官方文档后才恍然大悟。
好吧,我承认,这篇文章,与其说分享经验,不如说是吐槽公司IT环境。
强迫症犯了,
id
和
name
重复出现,不能忍。
进阶版如下:
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(untagged)]
enum ValueType {
Empty,
String(String),
Array(Vec<String>),
impl Default for ValueType {
fn default() -> Self {
Self::Empty
#[derive(Clone, Debug, Deserialize)]
struct Composite {
id:u32,
name:String,
#[serde(default)]
value: ValueType,
#[test]
pub fn test_composite_deserialize() -> serde_json::Result<()> {
let a = r#"{"id": 1, "name": "empty"}"#;
let b = r#"{"id": 2, "name": "single", "value": "string"}"#;
let c = r#"{"id": 3, "name": "multiple", "multiple": true, "value": ["first", "second"]}"#;
let mut v: Composite = serde_json::from_str(a)?;
assert_eq!(v.id, 1);
assert_eq!(v.name, "empty");
assert_eq!(v.value, ValueType::Empty);
v = serde_json::from_str(b)?;
assert_eq!(v.id, 2);
assert_eq!(v.name, "single");
assert_eq!(v.value, ValueType::String("string".to_owned()));
let mut data = Vec::<String>::new();
data.push("first".to_string());
data.push("second".to_string());
v = serde_json::from_str(c)?;
assert_eq!(v.id, 3);