我对于如何优雅且轻松地解析消息感到非常印象serde
深刻serde_json
:
{"msg_type": "asauce", "aaa": 3, "bbb": 14}
{"msg_type": "csyrup", "ccc": 10, "ddd": 20}
// this works!
#[derive(serde::Deserialize, PartialEq, Debug)]
struct AppleSauce {
aaa: u8,
bbb: u8,
}
#[derive(serde::Deserialize, PartialEq, Debug)]
struct ChocolateSyrup {
ccc: u8,
ddd: u8,
}
#[derive(serde::Deserialize, PartialEq, Debug)]
#[serde(tag = "msg_type")]
enum Sauce {
#[serde(rename = "asauce")]
AppleSauce(AppleSauce),
#[serde(rename = "csyrup")]
ChocolateSyrup(ChocolateSyrup),
}
#[test]
fn deserialise_inner_applesauce() {
let json = r#"{"msg_type": "asauce", "aaa": 3, "bbb": 14}"#;
let expected = AppleSauce { aaa: 3, bbb: 14 };
let sauce: Sauce = serde_json::from_str(json).unwrap();
assert_eq!(sauce, Sauce::AppleSauce(expected));
}
但是我的消息有一个父键,我想摆脱它:
{"boilerplate": {"msg_type": "asauce", "aaa": 3, "bbb": 14}}
{"boilerplate": {"msg_type": "csyrup", "ccc": 10, "ddd": 20}}
目前我已经实现了一个from_str
方法:
impl Sauce {
pub fn from_str(msg: &str) -> Option<Sauce> {
let outer: serde_json::Value = serde_json::from_str(msg).ok()?;
let inner = &outer["boilerplate"];
serde_json::from_value(inner.clone()).ok()
}
}
#[test]
fn deserialise_boilerplate_applesauce_with_helper_method() {
let json = r#"{"boilerplate": {"msg_type": "asauce", "aaa": 3, "bbb": 14}}"#;
let expected = AppleSauce { aaa: 3, bbb: 14 };
let sauce = Sauce::from_str(json).unwrap(); // not serde_json::from_str :(
assert_eq!(sauce, Sauce::AppleSauce(expected));
}
有没有办法用 serde 属性来省略父键,并消除自定义from_str
方法?
我只需要反序列化这些消息(但从我看到的 serde 来看,它可能也会免费序列化)。
不,没有属性可以做到这一点。您需要定义另一种类型,例如:
然后对其进行反序列化。或者,您需要一个自定义
Deserialize
实现,但这很快就会变得笨重,甚至比拥有另一个类型层还要笨重。您可以创建反映 JSON 的结构(
SauceSerde
和SauceWrapper
),然后impl From<SauceWrapper> for Sauce
处理转换。然后,您可以添加#[serde(from = "SauceWrapper")]
以便反序列化Sauce
使用 的SauceWrapper
反序列化逻辑。