384 lines
8.2 KiB
Rust
384 lines
8.2 KiB
Rust
use serde::Deserialize;
|
|
use serde_derive::Deserialize;
|
|
use serde_xml_rs::{from_str, Deserializer};
|
|
use simple_logger::SimpleLogger;
|
|
|
|
fn init_logger() {
|
|
let _ = SimpleLogger::new().init();
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct Item {
|
|
name: String,
|
|
source: String,
|
|
}
|
|
|
|
#[test]
|
|
fn simple_struct_from_attributes() {
|
|
init_logger();
|
|
|
|
let s = r##"
|
|
<item name="hello" source="world.rs" />
|
|
"##;
|
|
|
|
let item: Item = from_str(s).unwrap();
|
|
|
|
assert_eq!(
|
|
item,
|
|
Item {
|
|
name: "hello".to_string(),
|
|
source: "world.rs".to_string(),
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_roots_attributes() {
|
|
init_logger();
|
|
|
|
let s = r##"
|
|
<item name="hello" source="world.rs" />
|
|
<item name="hello" source="world.rs" />
|
|
"##;
|
|
|
|
let item: Vec<Item> = from_str(s).unwrap();
|
|
|
|
assert_eq!(
|
|
item,
|
|
vec![
|
|
Item {
|
|
name: "hello".to_string(),
|
|
source: "world.rs".to_string(),
|
|
},
|
|
Item {
|
|
name: "hello".to_string(),
|
|
source: "world.rs".to_string(),
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn simple_struct_from_attribute_and_child() {
|
|
init_logger();
|
|
|
|
let s = r##"
|
|
<item name="hello">
|
|
<source>world.rs</source>
|
|
</item>
|
|
"##;
|
|
|
|
let item: Item = from_str(s).unwrap();
|
|
|
|
assert_eq!(
|
|
item,
|
|
Item {
|
|
name: "hello".to_string(),
|
|
source: "world.rs".to_string(),
|
|
}
|
|
);
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct Project {
|
|
name: String,
|
|
|
|
#[serde(rename = "item", default)]
|
|
items: Vec<Item>,
|
|
}
|
|
|
|
#[test]
|
|
fn nested_collection() {
|
|
init_logger();
|
|
|
|
let s = r##"
|
|
<project name="my_project">
|
|
<item name="hello1" source="world1.rs" />
|
|
<item name="hello2" source="world2.rs" />
|
|
</project>
|
|
"##;
|
|
|
|
let project: Project = from_str(s).unwrap();
|
|
|
|
assert_eq!(
|
|
project,
|
|
Project {
|
|
name: "my_project".to_string(),
|
|
items: vec![
|
|
Item {
|
|
name: "hello1".to_string(),
|
|
source: "world1.rs".to_string(),
|
|
},
|
|
Item {
|
|
name: "hello2".to_string(),
|
|
source: "world2.rs".to_string(),
|
|
},
|
|
],
|
|
}
|
|
);
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
enum MyEnum {
|
|
A(String),
|
|
B { name: String, flag: bool },
|
|
C,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct MyEnums {
|
|
#[serde(rename = "$value")]
|
|
items: Vec<MyEnum>,
|
|
}
|
|
|
|
#[test]
|
|
fn collection_of_enums() {
|
|
init_logger();
|
|
|
|
let s = r##"
|
|
<enums>
|
|
<A>test</A>
|
|
<B name="hello" flag="true" />
|
|
<C />
|
|
</enums>
|
|
"##;
|
|
|
|
let project: MyEnums = from_str(s).unwrap();
|
|
|
|
assert_eq!(
|
|
project,
|
|
MyEnums {
|
|
items: vec![
|
|
MyEnum::A("test".to_string()),
|
|
MyEnum::B {
|
|
name: "hello".to_string(),
|
|
flag: true,
|
|
},
|
|
MyEnum::C,
|
|
],
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn out_of_order_collection() {
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct Collection {
|
|
a: Vec<A>,
|
|
b: Vec<B>,
|
|
c: C,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct A {
|
|
name: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct B {
|
|
name: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct C {
|
|
name: String,
|
|
}
|
|
|
|
init_logger();
|
|
|
|
let in_xml = r#"
|
|
<collection>
|
|
<a name="a1" />
|
|
<a name="a2" />
|
|
<b name="b1" />
|
|
<a name="a3" />
|
|
<c name="c" />
|
|
<b name="b2" />
|
|
<a name="a4" />
|
|
</collection>
|
|
"#;
|
|
|
|
let should_be = Collection {
|
|
a: vec![
|
|
A { name: "a1".into() },
|
|
A { name: "a2".into() },
|
|
A { name: "a3".into() },
|
|
A { name: "a4".into() },
|
|
],
|
|
b: vec![B { name: "b1".into() }, B { name: "b2".into() }],
|
|
c: C { name: "c".into() },
|
|
};
|
|
|
|
let mut de = Deserializer::new_from_reader(in_xml.as_bytes()).non_contiguous_seq_elements(true);
|
|
let actual = Collection::deserialize(&mut de).unwrap();
|
|
|
|
assert_eq!(should_be, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn nested_out_of_order_collection() {
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct OuterCollection {
|
|
a: A,
|
|
inner: Vec<InnerCollection>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct InnerCollection {
|
|
b: Vec<B>,
|
|
c: Vec<C>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct A {
|
|
name: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct B {
|
|
name: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct C {
|
|
name: String,
|
|
}
|
|
|
|
init_logger();
|
|
|
|
let in_xml = r#"
|
|
<collection>
|
|
<inner>
|
|
<b name="b1" />
|
|
<c name="c1" />
|
|
<b name="b2" />
|
|
<c name="c2" />
|
|
</inner>
|
|
<a name="a" />
|
|
<inner>
|
|
<c name="c3" />
|
|
<b name="b3" />
|
|
<c name="c4" />
|
|
<b name="b4" />
|
|
</inner>
|
|
</collection>
|
|
"#;
|
|
|
|
let should_be = OuterCollection {
|
|
a: A { name: "a".into() },
|
|
inner: vec![
|
|
InnerCollection {
|
|
b: vec![B { name: "b1".into() }, B { name: "b2".into() }],
|
|
c: vec![C { name: "c1".into() }, C { name: "c2".into() }],
|
|
},
|
|
InnerCollection {
|
|
b: vec![B { name: "b3".into() }, B { name: "b4".into() }],
|
|
c: vec![C { name: "c3".into() }, C { name: "c4".into() }],
|
|
},
|
|
],
|
|
};
|
|
|
|
let mut de = Deserializer::new_from_reader(in_xml.as_bytes()).non_contiguous_seq_elements(true);
|
|
let actual = OuterCollection::deserialize(&mut de).unwrap();
|
|
|
|
assert_eq!(should_be, actual);
|
|
}
|
|
|
|
#[test]
|
|
fn out_of_order_tuple() {
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct Collection {
|
|
val: (A, B, C),
|
|
other: A,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct A {
|
|
name_a: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct B {
|
|
name_b: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct C {
|
|
name_c: String,
|
|
}
|
|
|
|
init_logger();
|
|
|
|
let in_xml = r#"
|
|
<collection>
|
|
<val name_a="a1" />
|
|
<val name_b="b" />
|
|
<other name_a="a2" />
|
|
<val name_c="c" />
|
|
</collection>
|
|
"#;
|
|
|
|
let should_be = Collection {
|
|
val: (
|
|
A {
|
|
name_a: "a1".into(),
|
|
},
|
|
B { name_b: "b".into() },
|
|
C { name_c: "c".into() },
|
|
),
|
|
other: A {
|
|
name_a: "a2".into(),
|
|
},
|
|
};
|
|
|
|
let mut de = Deserializer::new_from_reader(in_xml.as_bytes()).non_contiguous_seq_elements(true);
|
|
let actual = Collection::deserialize(&mut de).unwrap();
|
|
|
|
assert_eq!(should_be, actual);
|
|
}
|
|
|
|
/// Ensure that identically-named elements at different depths are not deserialized as if they were
|
|
/// at the same depth.
|
|
#[test]
|
|
fn nested_collection_repeated_elements() {
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct OuterCollection {
|
|
a: Vec<A>,
|
|
inner: Inner,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct Inner {
|
|
a: A,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, PartialEq)]
|
|
struct A {
|
|
name: String,
|
|
}
|
|
|
|
init_logger();
|
|
|
|
let in_xml = r#"
|
|
<collection>
|
|
<a name="a1" />
|
|
<inner>
|
|
<a name="a2" />
|
|
</inner>
|
|
<a name="a3" />
|
|
</collection>
|
|
"#;
|
|
|
|
let should_be = OuterCollection {
|
|
a: vec![A { name: "a1".into() }, A { name: "a3".into() }],
|
|
inner: Inner {
|
|
a: A { name: "a2".into() },
|
|
},
|
|
};
|
|
|
|
let mut de = Deserializer::new_from_reader(in_xml.as_bytes()).non_contiguous_seq_elements(true);
|
|
let actual = OuterCollection::deserialize(&mut de).unwrap();
|
|
|
|
assert_eq!(should_be, actual);
|
|
}
|