1# Make a struct or enum parseable from XML
2
3This derives the [`FromXml`] trait on a struct or enum. It is the counterpart
4to [`macro@AsXml`].
5
6## Example
7
8```rust
9# use xso::FromXml;
10#[derive(FromXml, Debug, PartialEq)]
11#[xml(namespace = "urn:example", name = "foo")]
12struct Foo;
13
14let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'/>").unwrap();
15assert_eq!(foo, Foo);
16```
17
18## Attributes
19
20The derive macros need additional information, such as XML namespaces and
21names to match. This must be specified via key-value pairs on the type or
22fields the derive macro is invoked on. These key-value pairs are specified as
23Rust attributes. In order to disambiguate between XML attributes and Rust
24attributes, we are going to refer to Rust attributes using the term *meta*
25instead, which is consistent with the Rust language reference calling that
26syntax construct *meta*.
27
28All key-value pairs interpreted by these derive macros must be wrapped in a
29`#[xml( ... )]` *meta*.
30
31The values associated with the keys may be of different types, defined as
32such:
33
34- *path*: A Rust path, like `some_crate::foo::Bar`. Note that `foo` on its own
35 is also a path.
36- *string literal*: A string literal, like `"hello world!"`.
37- *type*: A Rust type.
38- *ident*: A Rust identifier.
39- flag: Has no value. The key's mere presence has relevance and it must not be
40 followed by a `=` sign.
41
42### Struct meta
43
44The following keys are defined on structs:
45
46| Key | Value type | Description |
47| --- | --- | --- |
48| `namespace` | *string literal* or *path* | The XML element namespace to match. If it is a *path*, it must point at a `&'static str`. |
49| `name` | *string literal* or *path* | The XML element name to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
50| `builder` | optional *ident* | The name to use for the generated builder type. |
51| `iterator` | optional *ident* | The name to use for the generated iterator type. |
52
53Note that the `name` value must be a valid XML element name, without colons.
54The namespace prefix, if any, is assigned automatically at serialisation time
55and cannot be overridden. The following will thus not compile:
56
57```compile_fail
58# use xso::FromXml;
59#[derive(FromXml, Debug, PartialEq)]
60#[xml(namespace = "urn:example", name = "fnord:foo")] // colon not allowed
61struct Foo;
62```
63
64If `builder` or `iterator` are given, the respective generated types will use
65the given names instead of names chosen by the derive macro implementation.
66Helper types will use these names as prefix. The exact names of helper types
67are implementation defined, which is why any type name starting with the
68identifiers passed to either of these keys is considered reserved.
69
70By default, the builder type uses the type's name suffixed with
71`FromXmlBuilder` and the iterator type uses the type's name suffixed with
72`AsXmlIterator`.
73
74### Field meta
75
76For fields, the *meta* consists of a nested meta inside the `#[xml(..)]` meta,
77the identifier of which controls *how* the field is mapped to XML, while the
78contents control the parameters of that mapping.
79
80The following mapping types are defined:
81
82| Type | Description |
83| --- | --- |
84| [`attribute`](#attribute-meta) | Map the field to an XML attribute on the struct's element |
85| [`child`](#child-meta) | Map the field to a child element |
86| [`text`](#text-meta) | Map the field to the text content of the struct's element |
87
88#### `attribute` meta
89
90The `attribute` meta causes the field to be mapped to an XML attribute of the
91same name. For `FromXml`, the field's type must implement [`FromXmlText`] and
92for `AsXml`, the field's type must implement [`AsOptionalXmlText`].
93
94The following keys can be used inside the `#[xml(attribute(..))]` meta:
95
96| Key | Value type | Description |
97| --- | --- | --- |
98| `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. |
99| `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`. |
100| `default` | flag | If present, an absent attribute will substitute the default value instead of raising an error. |
101
102If the `name` key contains a namespace prefix, it must be one of the prefixes
103defined as built-in in the XML specifications. That prefix will then be
104expanded to the corresponding namespace URI and the value for the `namespace`
105key is implied. Mixing a prefixed name with an explicit `namespace` key is
106not allowed.
107
108The `attribute` meta also supports a shorthand syntax,
109`#[xml(attribute = ..)]`, where the value is treated as the value for the
110`name` key (with optional prefix as described above, and unnamespaced
111otherwise).
112
113If `default` is specified and the attribute is absent in the source, the value
114is generated using [`std::default::Default`], requiring the field type to
115implement the `Default` trait for a `FromXml` derivation. `default` has no
116influence on `AsXml`.
117
118##### Example
119
120```rust
121# use xso::FromXml;
122#[derive(FromXml, Debug, PartialEq)]
123#[xml(namespace = "urn:example", name = "foo")]
124struct Foo {
125 #[xml(attribute)]
126 a: String,
127 #[xml(attribute = "bar")]
128 b: String,
129 #[xml(attribute(name = "baz"))]
130 c: String,
131 #[xml(attribute(namespace = "urn:example", name = "fnord"))]
132 d: String,
133 #[xml(attribute = "xml:lang")]
134 e: String,
135};
136
137let foo: Foo = xso::from_bytes(b"<foo
138 xmlns='urn:example'
139 a='1' bar='2' baz='3'
140 xmlns:tns0='urn:example' tns0:fnord='4'
141 xml:lang='5'
142/>").unwrap();
143assert_eq!(foo, Foo {
144 a: "1".to_string(),
145 b: "2".to_string(),
146 c: "3".to_string(),
147 d: "4".to_string(),
148 e: "5".to_string(),
149});
150```
151
152#### `child` meta
153
154The `child` meta causes the field to be mapped to a child element of the
155element.
156
157| Key | Value type | Description |
158| --- | --- | --- |
159| `default` | flag | If present, an absent child will substitute the default value instead of raising an error. |
160
161The field's type must implement [`FromXml`] in order to derive `FromXml` and
162[`AsXml`] in order to derive `AsXml`.
163
164If `default` is specified and the child is absent in the source, the value
165is generated using [`std::default::Default`], requiring the field type to
166implement the `Default` trait for a `FromXml` derivation. `default` has no
167influence on `AsXml`.
168
169##### Example
170
171```rust
172# use xso::FromXml;
173#[derive(FromXml, Debug, PartialEq)]
174#[xml(namespace = "urn:example", name = "child")]
175struct Child {
176 #[xml(attribute = "some-attr")]
177 some_attr: String,
178}
179
180#[derive(FromXml, Debug, PartialEq)]
181#[xml(namespace = "urn:example", name = "parent")]
182struct Parent {
183 #[xml(attribute)]
184 foo: String,
185
186 #[xml(child)]
187 bar: Child,
188}
189
190let parent: Parent = xso::from_bytes(b"<parent
191 xmlns='urn:example'
192 foo='hello world!'
193><child some-attr='within'/></parent>").unwrap();
194assert_eq!(parent, Parent {
195 foo: "hello world!".to_owned(),
196 bar: Child { some_attr: "within".to_owned() },
197});
198```
199
200#### `text` meta
201
202The `text` meta causes the field to be mapped to the text content of the
203element.
204
205| Key | Value type | Description |
206| --- | --- | --- |
207| `codec` | *type* | Optional [`TextCodec`] implementation which is used to encode or decode the field. |
208
209If `codec` is given, the given `codec` must implement
210[`TextCodec<T>`][`TextCodec`] where `T` is the type of the field.
211
212If `codec` is *not* given, the field's type must implement [`FromXmlText`] for
213`FromXml` and for `AsXml`, the field's type must implement [`AsXmlText`].
214
215The `text` meta also supports a shorthand syntax, `#[xml(text = ..)]`, where
216the value is treated as the value for the `codec` key (with optional prefix as
217described above, and unnamespaced otherwise).
218
219Only a single field per struct may be annotated with `#[xml(text)]` at a time,
220to avoid parsing ambiguities. This is also true if only `AsXml` is derived on
221a field, for consistency.
222
223##### Example without codec
224
225```rust
226# use xso::FromXml;
227#[derive(FromXml, Debug, PartialEq)]
228#[xml(namespace = "urn:example", name = "foo")]
229struct Foo {
230 #[xml(text)]
231 a: String,
232};
233
234let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'>hello</foo>").unwrap();
235assert_eq!(foo, Foo {
236 a: "hello".to_string(),
237});
238```
239
240##### Example with codec
241
242```rust
243# use xso::FromXml;
244#[derive(FromXml, Debug, PartialEq)]
245#[xml(namespace = "urn:example", name = "foo")]
246struct Foo {
247 #[xml(text = xso::text::EmptyAsNone)]
248 a: Option<String>,
249};
250
251let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'/>").unwrap();
252assert_eq!(foo, Foo {
253 a: None,
254});
255```