Introduction to XMLity
XMLity is a (de)serialization library for XML, inspired by serde
and improves upon XML (de)serialization libraries such as yaserde
and quick-xml
by providing a more flexible API that is more powerful, utilising primairly a trial and error approach to parsing XML.
Want to get started? Click here.
Comparison
Why does XMLity need to exist? Doesn't other XML-parsers/deserializers exist in Rust and why should I use/contribute to XMLity instead? To answer these questions, we've provided a feature chart below:
If something stated here is wrong, please submit a PR! It is not guaranteed to be correct, but should be a pretty fair description. The list tries to rank the features in order of importance, but it is ofcourse subjective.Feature | XMLity | Yaserde | quick-xml ( serde feature) |
---|---|---|---|
Namespace support | Yes | Yes | No |
Enum string value | Yes | Yes | Yes |
Enum number value | No? | Yes | Yes |
Trial and error deserialization | Yes | No | No * |
Standalone named elements (inherit element names from types) | Yes | No | No |
Order-based deserialization | Yes | No | Yes |
XSD Generator | No + | Yes | Yes |
Multiple reader/writer implementation support | Yes ** | No | No |
+ Being worked on.
? Planned.
* While quick-xml
has partial support, it is insufficient for any range of possible values. It only supports textual nodes, meaning trial and error deserialization is not possible for proper elements.
** While XMLity does have support for multiple readers/writers, the only current one that exists is the official one using quick-xml
, even if others are supported due to the data-model being completely deconnected from any other library. This should change in the near future as support for xml-rs
is planned.
Getting started
- Add
xmlity
dependency to yourCargo.toml
file:
[dependencies]
xmlity = { version = "0.1", features = ["derive"] }
# You can use any XML library you want, but the officially supported one is `quick-xml` using the `xmlity-quick-xml` crate.
xmlity-quick-xml = "0.1"
- Define your data model:
#![allow(unused)] fn main() { extern crate xmlity; extern crate xmlity_derive; extern crate xmlity_quick_xml; use xmlity::{Serialize, Deserialize};; use xmlity_derive::{Serialize, Deserialize}; #[derive(Serialize, Deserialize)] #[xelement(name = "name")] struct Name(String); #[derive(Serialize, Deserialize)] #[xelement(name = "age")] struct Age(u8); #[derive(Serialize, Deserialize)] #[xelement(name = "person")] struct Person { name: Name, age: Age, } let person = Person { name: Name("John".to_string()), age: Age(42), }; let xml = xmlity_quick_xml::to_string(&person).expect("Failed to serialize"); assert_eq!(xml, r#"<person><name>John</name><age>42</age></person>"#); let person: Person = xmlity_quick_xml::from_str(&xml).expect("Failed to deserialize"); assert_eq!(person.name.0, "John"); assert_eq!(person.age.0, 42); }
The data model
The data model of Xmlity is heavily inspired by serde.
Elements
The data-model of elements in Xmlity is built as a two-stage design, where the first stage of (de)serialization handles attributes and the second handles children. This is enforced by types so that both readers and writers can read in a logical direction, not having to jump between handling attributes and child elements.
Attributes
This section has not been written yet, but you're very welcome to contribute!
Groups
This section has not been written yet, but you're very welcome to contribute!
Derive details
Serialize
/Deserialize
- #[xelement(...)]
on structs
The #[xelement(...)]
attribute can be used to serialize/deserialize a struct as an XML element.
name = "..."
and namespace = "..."
If name
is not specified, the name of the struct will be used.
These attributes can be used to require that an element has a specific name and namespace. If the namespace is not specified, it will be defaulted to the blank namespace.
deserialize_any_name = true/false
If deserialize_any_name
is set to true
, the element will be deserialized regardless of its name. This is useful for elements that can have multiple names, or for elements that are used in multiple contexts.
attribute_order = "..."
and children_order = "..."
By default, elements in XMLity are deserialized regardless of the order of the inputs, but this can be changed using the attribute_order
and children_order
attributes. These attributes change elements to require that the inputs be in the same order as the fields in the struct. The possible values are as follows:
loose
: Elements must be in order, but incorrect elements can be interspersed between them (ifallow_unknown_attributes
/allow_unknown_children
is enabled).none
: Elements can be in any order (default).
preferred_prefix = "..."
Thia field is used to specify the preferred prefix for the element. This is used when serializing the element to XML. If the element is not in the default namespace, the preferred prefix will be used. If the element is in the default namespace, the preferred prefix will be ignored.
enforce_prefix = true/false
This field requires preferred_prefix
to be specified. This field encorces that the element will always use the specified prefix when serialized to XML.
Serialize
/Deserialize
- #[xvalue(...)]
on enums
The #[xvalue(...)]
attribute can be used to serialize/deserialize an enum as a text value.
rename_all = "..."
This field is used to specify the format of the text value. The possible values are "lowercase"
, "UPPERCASE"
, "PascalCase"
, "camelCase"
, "snake_case"
, "SCREAMING_SNAKE_CASE"
, "kebab-case"
, "SCREAMING-KEBAB-CASE"
.
allow_cdata = true/false
Defaults to true. If true, the text value can be deserialized from a CDATA section.
allow_text = true/false
Defaults to true. If true, the text value can be deserialized from a text node.
SerializationGroup
/DeserializationGroup
- #[xgroup(...)]
on structs
The SerializationGroup
and DeserializationGroup
traits are used to define groups of elements that can be serialized and deserialized together. This is useful for elements that are always used together, or for elements that are part of a larger structure.
attribute_order = "..."
and children_order = "..."
By default, groups in XMLity are deserialized regardless of the order of the inputs, but this can be changed using the attribute_order
and children_order
attributes. These attributes change groups to require that the inputs be in the same order as the fields in the struct. The possible values are as follows:
strict
: Elements/groups must be in order, and no other elements can be interspersed between them.loose
: Elements/groups must be in order, but incorrect elements can be interspersed between them. This can include elements in parent groups/elements.none
: Elements/groups can be in any order (default).
This behaviour works on nested levels, so if you have an element with children_order = "loose"
, and a group with children_order = "none"
inside it, the elements listed in the group can be in any order, but they must be in order with respect to the other elements in the parent element.
Serialize
/Deserialize
- Nothing on enums
All variants must have exactly one unnamed field, and the type of that field must implement Serialize
/Deserialize
.
When serializing, it will try to serialize each type in order of the variants. If it succeeds, it will wrap the serialized value in the variant and return it.
When deserializing, it deserializes the field and then wraps it in the variant.
Custom serialization
This section has not been written yet, but you're very welcome to contribute!
Implementing Serialize
This section has not been written yet, but you're very welcome to contribute!
Implementing SerializeAttribute
This section has not been written yet, but you're very welcome to contribute!
Implementing Deserialize
This section has not been written yet, but you're very welcome to contribute!
Implementing a Serializer
or Deserializer
This section has not been written yet, but you're very welcome to contribute!
Implementing a Serializer
This section has not been written yet, but you're very welcome to contribute!
For an example, see xmlity-quick-xml.
Implementing a Deserializer
This section has not been written yet, but you're very welcome to contribute!
For an example, see xmlity-quick-xml.
Examples
This section has not been written yet, but you're very welcome to contribute!
Feature flags
Some features of XMLity are not enabled by default but can instead be enabled using feature flags.
features = ["derive"]
Enables the proc-macros covered in Derive details.