2024-06-21 15:53:37 +00:00
# Make a struct or enum parseable from XML
This derives the [`FromXml`] trait on a struct or enum. It is the counterpart
2024-07-09 15:01:42 +00:00
to [`macro@AsXml`].
2024-06-21 15:53:37 +00:00
## Example
```rust
# use xso::FromXml;
#[derive(FromXml, Debug, PartialEq)]
2024-06-22 07:30:45 +00:00
#[xml(namespace = "urn:example", name = "foo")]
2024-06-21 15:53:37 +00:00
struct Foo;
let foo: Foo = xso::from_bytes(b"< foo xmlns = 'urn:example' / > ").unwrap();
assert_eq!(foo, Foo);
```
## Attributes
2024-06-23 07:06:32 +00:00
The derive macros need additional information, such as XML namespaces and
names to match. This must be specified via key-value pairs on the type or
fields the derive macro is invoked on. These key-value pairs are specified as
Rust attributes. In order to disambiguate between XML attributes and Rust
attributes, we are going to refer to Rust attributes using the term *meta*
instead, which is consistent with the Rust language reference calling that
syntax construct *meta* .
2024-06-21 15:53:37 +00:00
All key-value pairs interpreted by these derive macros must be wrapped in a
2024-06-23 07:06:32 +00:00
`#[xml( ... )]` *meta* .
2024-06-26 13:56:43 +00:00
The values associated with the keys may be of different types, defined as
such:
- *path*: A Rust path, like `some_crate::foo::Bar` . Note that `foo` on its own
is also a path.
- *string literal*: A string literal, like `"hello world!"` .
2024-06-26 16:26:13 +00:00
- *type*: A Rust type.
2024-06-26 13:56:43 +00:00
- flag: Has no value. The key's mere presence has relevance and it must not be
followed by a `=` sign.
2024-06-23 07:06:32 +00:00
### Struct meta
The following keys are defined on structs:
2024-06-21 15:53:37 +00:00
| Key | Value type | Description |
| --- | --- | --- |
2024-06-22 07:30:45 +00:00
| `namespace` | *string literal* or *path* | The XML element namespace to match. If it is a *path* , it must point at a `&'static str` . |
2024-06-22 07:25:42 +00:00
| `name` | *string literal* or *path* | The XML element name to match. If it is a *path* , it must point at a `&'static NcNameStr` . |
2024-06-21 15:53:37 +00:00
2024-06-22 07:13:47 +00:00
Note that the `name` value must be a valid XML element name, without colons.
The namespace prefix, if any, is assigned automatically at serialisation time
and cannot be overridden. The following will thus not compile:
```compile_fail
# use xso::FromXml;
#[derive(FromXml, Debug, PartialEq)]
2024-06-22 07:30:45 +00:00
#[xml(namespace = "urn:example", name = "fnord:foo")] // colon not allowed
2024-06-22 07:13:47 +00:00
struct Foo;
```
2024-06-23 07:06:32 +00:00
### Field meta
2024-06-21 15:53:37 +00:00
2024-06-23 07:06:32 +00:00
For fields, the *meta* consists of a nested meta inside the `#[xml(..)]` meta,
the identifier of which controls *how* the field is mapped to XML, while the
contents control the parameters of that mapping.
2024-06-21 15:53:37 +00:00
2024-06-23 07:06:32 +00:00
The following mapping types are defined:
| Type | Description |
| --- | --- |
| [`attribute` ](#attribute-meta ) | Map the field to an XML attribute on the struct's element |
2024-06-26 15:54:36 +00:00
| [`text` ](#text-meta ) | Map the field to the text content of the struct's element |
2024-06-23 07:06:32 +00:00
#### `attribute` meta
2024-06-23 08:09:59 +00:00
The `attribute` meta causes the field to be mapped to an XML attribute of the
2024-06-25 15:37:03 +00:00
same name. For `FromXml` , the field's type must implement [`FromXmlText`] and
2024-07-09 15:01:42 +00:00
for `AsXml` , the field's type must implement [`AsOptionalXmlText`].
2024-06-23 08:09:59 +00:00
The following keys can be used inside the `#[xml(attribute(..))]` meta:
| Key | Value type | Description |
| --- | --- | --- |
2024-06-24 05:28:04 +00:00
| `namespace` | *string literal* or *path* | The optional namespace of the XML attribute to match. If it is a *path* , it must point at a `&'static str` . Note that attributes, unlike elements, are unnamespaced by default. |
2024-06-23 08:09:59 +00:00
| `name` | *string literal* or *path* | The name of the XML attribute to match. If it is a *path* , it must point at a `&'static NcNameStr` . |
2024-06-26 13:56:43 +00:00
| `default` | flag | If present, an absent attribute will substitute the default value instead of raising an error. |
2024-06-23 08:09:59 +00:00
2024-06-24 05:43:51 +00:00
If the `name` key contains a namespace prefix, it must be one of the prefixes
defined as built-in in the XML specifications. That prefix will then be
expanded to the corresponding namespace URI and the value for the `namespace`
key is implied. Mixing a prefixed name with an explicit `namespace` key is
not allowed.
2024-06-23 08:09:59 +00:00
The `attribute` meta also supports a shorthand syntax,
`#[xml(attribute = ..)]` , where the value is treated as the value for the
2024-06-24 05:43:51 +00:00
`name` key (with optional prefix as described above, and unnamespaced
otherwise).
2024-06-23 08:09:59 +00:00
2024-06-26 13:56:43 +00:00
If `default` is specified and the attribute is absent in the source, the value
is generated using [`std::default::Default`], requiring the field type to
implement the `Default` trait for a `FromXml` derivation. `default` has no
2024-07-09 15:01:42 +00:00
influence on `AsXml` .
2024-06-26 13:56:43 +00:00
2024-06-23 08:09:59 +00:00
##### Example
```rust
# use xso::FromXml;
#[derive(FromXml, Debug, PartialEq)]
#[xml(namespace = "urn:example", name = "foo")]
struct Foo {
#[xml(attribute)]
a: String,
#[xml(attribute = "bar")]
b: String,
#[xml(attribute(name = "baz"))]
c: String,
2024-06-24 05:28:04 +00:00
#[xml(attribute(namespace = "urn:example", name = "fnord"))]
d: String,
2024-06-24 05:43:51 +00:00
#[xml(attribute = "xml:lang")]
e: String,
2024-06-23 08:09:59 +00:00
};
2024-06-24 05:28:04 +00:00
let foo: Foo = xso::from_bytes(b"< foo
xmlns='urn:example'
a='1' bar='2' baz='3'
xmlns:tns0='urn:example' tns0:fnord='4'
2024-06-24 05:43:51 +00:00
xml:lang='5'
2024-06-24 05:28:04 +00:00
/>").unwrap();
2024-06-23 08:09:59 +00:00
assert_eq!(foo, Foo {
a: "1".to_string(),
b: "2".to_string(),
c: "3".to_string(),
2024-06-24 05:28:04 +00:00
d: "4".to_string(),
2024-06-24 05:43:51 +00:00
e: "5".to_string(),
2024-06-23 08:09:59 +00:00
});
```
2024-06-26 15:54:36 +00:00
#### `text` meta
The `text` meta causes the field to be mapped to the text content of the
2024-06-26 16:26:13 +00:00
element.
2024-06-26 15:54:36 +00:00
2024-06-26 16:26:13 +00:00
| Key | Value type | Description |
| --- | --- | --- |
| `codec` | *type* | Optional [`TextCodec`] implementation which is used to encode or decode the field. |
2024-06-26 15:54:36 +00:00
2024-06-26 16:26:13 +00:00
If `codec` is given, the given `codec` must implement
[`TextCodec< T > `][`TextCodec`] where `T` is the type of the field.
If `codec` is *not* given, the field's type must implement [`FromXmlText`] for
2024-07-09 15:01:42 +00:00
`FromXml` and for `AsXml` , the field's type must implement [`AsXmlText`].
2024-06-26 16:26:13 +00:00
The `text` meta also supports a shorthand syntax, `#[xml(text = ..)]` , where
the value is treated as the value for the `codec` key (with optional prefix as
described above, and unnamespaced otherwise).
Only a single field per struct may be annotated with `#[xml(text)]` at a time,
2024-07-09 15:01:42 +00:00
to avoid parsing ambiguities. This is also true if only `AsXml` is derived on
2024-06-26 16:26:13 +00:00
a field, for consistency.
##### Example without codec
2024-06-26 15:54:36 +00:00
```rust
# use xso::FromXml;
#[derive(FromXml, Debug, PartialEq)]
#[xml(namespace = "urn:example", name = "foo")]
struct Foo {
#[xml(text)]
a: String,
};
let foo: Foo = xso::from_bytes(b"< foo xmlns = 'urn:example' > hello< / foo > ").unwrap();
assert_eq!(foo, Foo {
a: "hello".to_string(),
});
```
2024-06-26 16:26:13 +00:00
##### Example with codec
```rust
# use xso::FromXml;
#[derive(FromXml, Debug, PartialEq)]
#[xml(namespace = "urn:example", name = "foo")]
struct Foo {
#[xml(text = xso::text::EmptyAsNone)]
a: Option< String > ,
};
let foo: Foo = xso::from_bytes(b"< foo xmlns = 'urn:example' / > ").unwrap();
assert_eq!(foo, Foo {
a: None,
});
```