Expand description
A serde-like library for rigorous XML (de)serialization.
instant-xml provides traits and derive macros for mapping XML to Rust types, with full support for XML namespaces and zero-copy deserialization.
§Quick Start
#[derive(Debug, PartialEq, FromXml, ToXml)]
struct Person {
name: String,
#[xml(attribute)]
age: u32,
}
let person = Person {
name: "Alice".to_string(),
age: 30,
};
let xml = to_string(&person).unwrap();
assert_eq!(xml, r#"<Person age="30"><name>Alice</name></Person>"#);
let deserialized: Person = from_str(&xml).unwrap();
assert_eq!(person, deserialized);§#[xml(...)] attribute reference
The #[xml(...)] attribute configures serialization and deserialization behavior
for the ToXml and FromXml derive macros.
§Container attributes
Applied to structs and enums using #[xml(...)]:
-
rename = "name"- renames the root element#[derive(ToXml)] #[xml(rename = "custom-name")] struct MyStruct { } assert_eq!(to_string(&MyStruct {}).unwrap(), "<custom-name />"); -
rename_all = "case"- transforms all field/variant names.Supported cases:
"lowercase","UPPERCASE","PascalCase","camelCase","snake_case","SCREAMING_SNAKE_CASE","kebab-case","SCREAMING-KEBAB-CASE".#[derive(ToXml)] #[xml(rename_all = "camelCase")] struct MyStruct { field_one: String, } let s = MyStruct { field_one: "value".to_string() }; assert_eq!(to_string(&s).unwrap(), "<MyStruct><fieldOne>value</fieldOne></MyStruct>"); -
ns("uri")orns("uri", prefix = "namespace")- configures XML namespacesNamespace URIs can be string literals or paths to constants. Prefixes may contain dashes and dots:
#[xml(ns(my-ns.v1 = "uri"))].#[derive(ToXml)] #[xml(ns("http://example.com"))] struct Root { } assert_eq!(to_string(&Root {}).unwrap(), r#"<Root xmlns="http://example.com" />"#); #[derive(ToXml)] #[xml(ns("http://example.com", xsi = XSI))] struct WithPrefix { } assert_eq!( to_string(&WithPrefix {}).unwrap(), r#"<WithPrefix xmlns="http://example.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />"# ); const XSI: &'static str = "http://www.w3.org/2001/XMLSchema-instance"; -
transparent(structs only) - inlines fields without wrapper element#[derive(ToXml)] #[xml(transparent)] struct Inline { foo: Foo, bar: Bar, } #[derive(ToXml)] struct Foo { } #[derive(ToXml)] struct Bar { } let inline = Inline { foo: Foo {}, bar: Bar {} }; assert_eq!(to_string(&inline).unwrap(), "<Foo /><Bar />"); -
scalar(enums only) - serializes variants as text content.The enum must only have unit variants.
#[derive(ToXml)] struct Container { status: Status, } #[derive(ToXml)] #[xml(scalar)] enum Status { Active, Inactive, } let c = Container { status: Status::Active }; assert_eq!(to_string(&c).unwrap(), "<Container><status>Active</status></Container>");Variants can use
#[xml(rename = "...")]or string/integer discriminants. -
forward(enums only) - forwards to inner type’s element name.Each variant must contain exactly one unnamed field.
#[derive(ToXml)] #[xml(forward)] enum Message { Request(Request), Response(Response), } #[derive(ToXml)] struct Request { } #[derive(ToXml)] struct Response { } let msg = Message::Request(Request {}); assert_eq!(to_string(&msg).unwrap(), "<Request />");
§Field attributes
Applied to struct fields using #[xml(...)]:
-
attribute- (de)serializes as XML attribute instead of child element#[derive(ToXml)] struct Element { #[xml(attribute)] id: String, } let elem = Element { id: "abc123".to_string() }; assert_eq!(to_string(&elem).unwrap(), r#"<Element id="abc123" />"#); -
direct- field contains element’s direct text content#[derive(ToXml)] struct Paragraph { #[xml(attribute)] lang: String, #[xml(direct)] text: String, } let p = Paragraph { lang: "en".to_string(), text: "Hello".to_string() }; assert_eq!(to_string(&p).unwrap(), r#"<Paragraph lang="en">Hello</Paragraph>"#); -
rename = "name"- renames the field’s element or attribute name -
ns("uri")- sets namespace for this specific field. Like the container-level attribute, this supports both string literals and constant paths. -
serialize_with = "path"- custom serialization function with signature:#[derive(ToXml)] struct Config { #[xml(serialize_with = "serialize_custom")] count: u32, } fn serialize_custom<W: fmt::Write + ?Sized>( value: &u32, serializer: &mut Serializer<'_, W>, ) -> Result<(), Error> { serializer.write_str(&format!("value: {}", value))?; Ok(()) } let config = Config { count: 42 }; assert_eq!(to_string(&config).unwrap(), "<Config>value: 42</Config>"); -
deserialize_with = "path"- custom deserialization function with signature:#[derive(FromXml, PartialEq, Debug)] struct Config { #[xml(deserialize_with = "deserialize_bool")] enabled: bool, } fn deserialize_bool<'xml>( accumulator: &mut <bool as FromXml<'xml>>::Accumulator, field: &'static str, deserializer: &mut Deserializer<'_, 'xml>, ) -> Result<(), Error> { if accumulator.is_some() { return Err(Error::DuplicateValue(field)); } let Some(s) = deserializer.take_str()? else { return Ok(()); }; *accumulator = Some(match s.as_ref() { "yes" => true, "no" => false, other => return Err(Error::UnexpectedValue( format!("expected 'yes' or 'no', got '{}'", other) )), }); deserializer.ignore()?; Ok(()) } let xml = "<Config><enabled>yes</enabled></Config>"; let config = from_str::<Config>(xml).unwrap(); assert_eq!(config.enabled, true); -
borrow- Borrows from input during deserialization. Automatically applies to top-level&strand&[u8]fields. Useful forCow<str>and similar types.#[derive(FromXml, PartialEq, Debug)] struct Borrowed<'a> { #[xml(borrow)] text: Cow<'a, str>, } let xml = "<Borrowed><text>Hello</text></Borrowed>"; let parsed = from_str::<Borrowed>(xml).unwrap(); assert_eq!(parsed.text, "Hello");
Structs§
Enums§
Traits§
- Accumulate
- A type implementing
Accumulate<T>is used to accumulate a value of typeT. - FromXml
- From
XmlOwned - ToXml