use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Thing {
pub x: u32,
pub y: f64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct NewThing {
pub x: u32,
pub y: f64,
pub z: Vec<u8>,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
enum ExternallyTaggedThings {
V1(Thing),
V2(NewThing),
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(tag = "version")]
enum InternallyTaggedThings {
V1(Thing),
V2(NewThing),
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(tag = "version", content = "c")]
enum AdjacentlyTaggedThings {
V1(Thing),
V2(NewThing),
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(untagged)]
enum UnTaggedThings {
V1(Thing),
V2(NewThing),
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_json() {
use serde_json::{from_str, to_string};
let x = ExternallyTaggedThings::V1(Thing { x: 1, y: 2.0 });
assert_eq!(to_string(&x).unwrap(), r#"{"V1":{"x":1,"y":2.0}}"#);
let y: ExternallyTaggedThings = from_str(r#"{"V1":{"x":1,"y":2.0}}"#).unwrap();
assert_eq!(x, y);
let x = InternallyTaggedThings::V1(Thing { x: 1, y: 2.0 });
assert_eq!(to_string(&x).unwrap(), r#"{"version":"V1","x":1,"y":2.0}"#);
let y: InternallyTaggedThings = from_str(r#"{"version":"V1","x":1,"y":2.0}"#).unwrap();
assert_eq!(x, y);
let x = AdjacentlyTaggedThings::V1(Thing { x: 1, y: 2.0 });
assert_eq!(
to_string(&x).unwrap(),
r#"{"version":"V1","c":{"x":1,"y":2.0}}"#
);
let y: AdjacentlyTaggedThings =
from_str(r#"{"version":"V1","c":{"x":1,"y":2.0}}"#).unwrap();
assert_eq!(x, y);
let x = UnTaggedThings::V1(Thing { x: 1, y: 2.0 });
assert_eq!(to_string(&x).unwrap(), r#"{"x":1,"y":2.0}"#);
let y: UnTaggedThings = from_str(r#"{"x":1,"y":2.0}"#).unwrap();
assert_eq!(x, y);
}
#[test]
fn test_sexp() {
use serde_lexpr::{from_str, to_string};
let x = ExternallyTaggedThings::V1(Thing { x: 1, y: 2.0 });
assert_eq!(to_string(&x).unwrap(), r#"(V1 (x . 1) (y . 2.0))"#);
let y: ExternallyTaggedThings = from_str(r#"(V1 (x . 1) (y . 2.0))"#).unwrap();
assert_eq!(x, y);
let x = InternallyTaggedThings::V1(Thing { x: 1, y: 2.0 });
assert_eq!(
to_string(&x).unwrap(),
r#"((version . "V1") (x . 1) (y . 2.0))"#
);
let y: InternallyTaggedThings =
from_str(r#"((version . "V1") (x . 1) (y . 2.0))"#).unwrap();
assert_eq!(x, y); // <==== FAILS HERE
let x = AdjacentlyTaggedThings::V1(Thing { x: 1, y: 2.0 });
assert_eq!(
to_string(&x).unwrap(),
r#"((version . "V1") (c (x . 1) (y . 2.0)))"#
);
let y: AdjacentlyTaggedThings =
from_str(r#"((version . "V1") (c (x . 1) (y . 2.0)))"#).unwrap();
assert_eq!(x, y); // <==== FAILS HERE
let x = UnTaggedThings::V1(Thing { x: 1, y: 2.0 });
assert_eq!(to_string(&x).unwrap(), r#"((x . 1) (y . 2.0))"#);
let y: UnTaggedThings = from_str(r#"((x . 1) (y . 2.0))"#).unwrap();
assert_eq!(x, y); // <==== FAILS HERE
}
}
fn main() {
println!("Hello, world!");
}
When serializing, enumeration payloads can be tagged in four ways:
#[serde(tag = "type")])#[serde(tag = "t", content = "c")])#[serde(untagged)])Internal tagging is particularly useful when you want to version your structures; you can maintain different structs in your code, and in serialized format, each will appear to have a field named "version" (or whatever). E.g.
If I write out V1, it will be something like ((version . "V1") (x . 1) (y . "blah")), but deserialization will fail.
Full test case-- click to expand
I've debugged the internally tagged case down to the point where I can see
Deserializer::deserialize_identifierbeing invoked (serde-lexpr/serc/value, line 259) on ("version" . "V1"), expect a symbol, and fail. That call is generated by thederive(Deserialize)code, so it seems like the place to change. But it's odd that there doesn't seem to be a natural place to check the "version" tag. Also, serde-json implements deserialize_identifier simply by reading a string-- seems like it, too would be broken here, so I'm still missing something.