# xmlserde
[![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT/Mit-blue.svg)](./LICENSE)
`xmlserde` is a tool for serializing or deserializing xml struct.
It is designed for [LogiSheets](https://github.com/proclml/LogiSheets), which is a spreadsheets application working on the browser.
You can check the detail of usage in the `workbook` directory or [here](https://github.com/logisky/LogiSheets/tree/master/crates/workbook).
## How to use `xmlserde`
`xmlserde` offers a range of macros that should suffice for most use cases. To utilize them, you need to include the following crates in your `Cargo.toml` file:
```toml
xmlserde = 0.10
xmlserde_derives = 0.10
```
### Deserialize
Start from deserializing would be easier to get closer to `xmlserde`.
Given the xml struct as below,
```xml
Tom
```
We can deserialize with these code:
```rs
use xmlserde_derives: XmlDerserialize
#[derive(XmlDeserialize)]
#[xmlserde(root = b"person")]
pub struct Person {
#[xmlserde(name=b"age", ty="attr")]
pub age: u8,
#[xmlserde(ty ="text")]
pub name: String,
}
fn deserialize_person() {
use xmlserde::xml_deserialize_from_str;
let s = r#"Tom"#;
let p = xml_deserialize_from_str(s).unwrap();
assert_eq!(p.age, 16);
assert_eq!(p.name, "Tom");
}
```
You are supposed to declare that where the deserializer is to look for the values.
The commonly available *type*s are **attr**, **text** and **child**. In the above example, we instruct program to navigate into the tag named `person` (using `xml_deserialize_from_str`), and to search for an attribute
with the key `age`. Additionally it specifies that the content of the text element represents the value of the field `name`.
You can specify the entry element for serialization/deserialization with xmlserde by using the annotation like `#[xmlserde(root = b"person")]`, thereby telling the program that the `person` element is the root for serde operations.
Below is an example illustrating how to deserialize a nested XML element:
```rs
#[derive(XmlDeserialize)]
#[xmlserde(root = b"person")]
pub struct Person {
#[xmlserde(name=b"age", ty="attr")]
pub age: u8,
#[xmlserde(name = b"lefty", ty ="attr", default = "default_lefty")]
pub lefty: bool,
#[xmlserde(name = b"name", ty ="child")]
pub name: Name,
}
#[derive(XmlDeserialize)]
pub struct Name {
#[xmlserde(name = b"zh", ty ="attr")]
pub zh: String,
#[xmlserde(name = b"en", ty ="attr")]
pub en: String,
}
fn deserialize_person() {
use xmlserde::xml_deserialize_from_str;
let s = r#""#;
let p = xml_deserialize_from_str(s).unwrap();
assert_eq!(p.age, 16);
assert_eq!(p.name.en, "Tom");
assert_eq!(p.lefty, false);
}
fn default_lefty() -> bool { false }
```
In the given example, we specify that the value of the `name` field is to be extracted from a child element tagged as ``.
Consequently, the program will navigate into the `` element and proceed with recursive deserialization.
Additionally, we specify that if the deserializer does not find a value for `lefty`, the default value for `lefty` should be set to false.
#### Vec
We support deserialize the fields whose types are `std::Vec`.
```rs
#[derive(XmlDeserialize)]
pub struct Pet {
// Fields
}
#[derive(XmlDeserialize)]
#[xmlserde(root = b"person")]
pub struct Person {
#[xmlserde(name = b"petCount", ty = "attr")]
pub pet_count: u8,
#[xmlserde(name = b"pet", ty = "child")]
pub pets: Vec
}
```
When the deserializer find the *pet* element, and it will know that this is an element of **pets**. You can even assign the capacity of the `Vec` with following:
```xml
#[xmlserde(name = b"pet", ty="child", vec_size=3)]
```
If the capacity is from an **attr**, you can:
```xml
#[xmlserde(name = b"pet", ty="child", vec_size="pet_count")]
```
#### Enum
We provide 2 patterns for deserializing `Enum`.
```rs
#[derive(XmlSerialize, XmlDeserialize)]
enum TestEnum {
#[xmlserde(name = b"testA")]
TestA(TestA),
#[xmlserde(name = b"testB")]
TestB(TestB),
}
#[derive(XmlSerialize, XmlDeserialize)]
#[xmlserde(root = b"personA")]
pub struct PersonA {
#[xmlserde(name = b"e", ty = "child")]
pub e: TestEnum
// Other fields
}
#[derive(XmlSerialize, XmlDeserialize)]
#[xmlserde(root = b"personB")]
pub struct PersonB {
#[xmlserde(ty = "untag")]
pub dummy: TestEnum
// Other fields
}
```
**PersonA** can be used to deserialize the xml struct like below:
```xml
```
or
```xml
```
And **PersonB** can be used to deserialize the xml which looks like:
```xml
```
or
```xml
```
You can use **untag** to **Option\** or **Vec\** where **T** is an **Enum**.
It means that the example below is legal:
```rust
#[derive(XmlSerialize, XmlDeserialize)]
#[xmlserde(root = b"personB")]
pub struct PersonB {
#[xmlserde(ty = "untag")]
pub dummy1: Enum1,
#[xmlserde(ty = "untag")]
pub dummy2: Option,
#[xmlserde(ty = "untag")]
pub dummy3: Vec,
// Other fields
}
```
#### Unparsed
In situations where certain XML elements are not immediately relevant, but you wish to retain them for future serialization, we offer the `Unparsed` struct
for this purpose.
```rs
use xmlserde::Unparsed;
#[derive(XmlDeserialize)]
pub struct Person {
#[xmlserde(name = b"educationHistory", ty = "child")]
pub education_history: Unparsed,
}
```
### Serialize
Serialization is largely similar to deserialization. However, there are several key features that require consideration.
- default values will be skipped serializing.
If it is a **struct**, it should be implemented `Eq` trait.
- If a struct has no **child** or **text**, the result of serializing will
look like this:
```xml
```
### Custom xmlserde
`xmlserde` offers the trait `XmlSerialize` and `XmlDeserialize`, allowing you
to dictate a struct's serialization and deserialization behavior by implementing
these traits.
At present, only built-in types are permitted for use as attributes. To enable custom types for use in attributes, you can implement the `XmlValue` trait on those types.
### Enum for string type
`xmlserde` also provides a macro called `xml_serde_enum` to serde `enum` for string type.
`xml_serde_enum` defines an `enum` and specifies the behavior of serialization and deserialization.
```rust
use xmlserde::xml_serde_enum;
xml_serde_enum!{
#[derive(Debug)]
Gender {
Male => "male",
Female => "female",
}
}
```