Estou realmente impressionado com a elegância e facilidade com que serde
consigo serde_json
analisar mensagens:
{"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));
}
Entretanto, minhas mensagens têm uma chave pai, da qual gostaria de me livrar:
{"boilerplate": {"msg_type": "asauce", "aaa": 3, "bbb": 14}}
{"boilerplate": {"msg_type": "csyrup", "ccc": 10, "ddd": 20}}
No momento implementei um from_str
método:
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));
}
Existe uma maneira de eliminar a chave pai com um atributo serde e eliminar o from_str
método personalizado?
Só preciso desserializar essas mensagens (mas pelo que vejo no serde, ele provavelmente seria serializado também de graça).
Não, não há nenhum atributo que faça isso. Você precisaria definir outro tipo, como:
E então desserialize isso em vez disso. Alternativamente, você precisaria de uma
Deserialize
implementação personalizada, mas isso fica difícil de manejar muito rápido, ainda mais do que ter outra camada de tipo.Você pode fazer structs (
SauceSerde
eSauceWrapper
) que refletem o JSON e, então,impl From<SauceWrapper> for Sauce
manipular a conversão. Então, você pode adicionar#[serde(from = "SauceWrapper")]
para que a desserializaçãoSauce
useSauceWrapper
a lógica de desserialização de .