obake
obake copied to clipboard
Deserialzing `untagged` versions should default to latest version (not earliest)
Hi! This library is so useful, but I'm failing to use it with serde_json
. I've tried the following way:
use serde_json::json;
use obake;
#[obake::versioned]
#[obake(version("0.1.0"))]
#[obake(version("0.2.0"))]
#[obake(version("0.3.0"))]
#[obake(derive(Debug, PartialEq, serde::Serialize, serde::Deserialize))]
#[obake(serde(untagged))]
#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
struct Character {
name: String,
age: u32,
#[obake(cfg(">=0.2.0"))]
height: f32,
#[obake(cfg(">=0.3.0"))]
weight: f32,
}
impl From<Character!["0.1.0"]> for Character!["0.2.0"] {
fn from(old: Character!["0.1.0"]) -> Self {
Self {
name: old.name,
age: old.age,
..Default::default()
}
}
}
impl From<Character!["0.2.0"]> for Character!["0.3.0"] {
fn from(old: Character!["0.2.0"]) -> Self {
Self {
name: old.name,
age: old.age,
height: old.height,
..Default::default()
}
}
}
fn main() {}
#[cfg(test)]
mod tests {
use serde_json::json;
use crate::*;
#[test]
fn from_2_to_3() {
let freeza_ser = serde_json::to_string_pretty(&json!({
"name": "Freeza",
"age": 32,
"height": 1.53,
})).unwrap();
dbg!(&freeza_ser);
let freeza: VersionedCharacter = serde_json::from_str(&freeza_ser).unwrap();
dbg!(&freeza);
let freeza: Character = freeza.into();
dbg!(&freeza);
let freeza_expected = Character {
name: "Freeza".into(),
age: 32,
height: 1.53,
weight: 0.,
};
assert_eq!(&freeza_expected, &freeza);
}
}
The output is:
failures:
---- tests::from_2_to_3 stdout ----
[src/main.rs:56] &freeza_ser = "{\n \"age\": 32,\n \"height\": 1.53,\n \"name\": \"Freeza\"\n}"
[src/main.rs:59] &freeza = Character_v0_1_0(
Character_v0_1_0 {
name: "Freeza",
age: 32,
},
)
[src/main.rs:61] &freeza = Character_v0_3_0 {
name: "Freeza",
age: 32,
height: 0.0,
weight: 0.0,
}
thread 'tests::from_2_to_3' panicked at 'assertion failed: `(left == right)`
left: `Character_v0_3_0 { name: "Freeza", age: 32, height: 1.53, weight: 0.0 }`,
right: `Character_v0_3_0 { name: "Freeza", age: 32, height: 0.0, weight: 0.0 }`', src/main.rs:69:9
So, the current behavior is that serde_json::from_str::<VersionedCharacter>(&freeza_ser)
is deserializing into the first version, and I want it to deserialize like the following:
fn character_parser(serialized_data: &String) -> Character {
if let Ok(data) = serde_json::from_str::<Character!["0.3.0"]>(&serialized_data) {
return data;
}
if let Ok(data) = serde_json::from_str::<Character!["0.2.0"]>(&serialized_data) {
let data: Character!["0.3.0"] = data.into();
return data;
}
if let Ok(data) = serde_json::from_str::<Character!["0.1.0"]>(&serialized_data) {
let data: Character!["0.2.0"] = data.into();
let data: Character!["0.3.0"] = data.into();
return data;
}
return Character::default();
}
Am I missing something crucial here? Is there any way for this lib to implement that parsing automatically?
Thank you!
This is very interesting thank you! I believe the issue has to do with the ambiguity caused by untagged
. The behaviour you're observing is that the deserializer is trying earlier versions first, when in actual fact we'd like it to try later versions first. I'll have a look at this as soon as possible!