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### Enum meta
75
76The following keys are defined on enums:
77
78| Key | Value type | Description |
79| --- | --- | --- |
80| `namespace` | *string literal* or *path* | The XML element namespace to match for this enum. If it is a *path*, it must point at a `&'static str`. |
81| `builder` | optional *ident* | The name to use for the generated builder type. |
82| `iterator` | optional *ident* | The name to use for the generated iterator type. |
83
84All variants of an enum live within the same namespace and are distinguished
85exclusively by their XML name within that namespace. The contents of the XML
86element (including attributes) is not inspected before selecting the variant
87when parsing XML.
88
89For details on `builder` and `iterator`, see the [Struct meta](#struct-meta)
90documentation above.
91
92#### Enum variant meta
93
94| Key | Value type | Description |
95| --- | --- | --- |
96| `name` | *string literal* or *path* | The XML element name to match for this variant. If it is a *path*, it must point at a `&'static NcNameStr`. |
97
98Note that the `name` value must be a valid XML element name, without colons.
99The namespace prefix, if any, is assigned automatically at serialisation time
100and cannot be overridden.
101
102#### Example
103
104```rust
105# use xso::FromXml;
106#[derive(FromXml, Debug, PartialEq)]
107#[xml(namespace = "urn:example")]
108enum Foo {
109 #[xml(name = "a")]
110 Variant1 {
111 #[xml(attribute)]
112 foo: String,
113 },
114 #[xml(name = "b")]
115 Variant2 {
116 #[xml(attribute)]
117 bar: String,
118 },
119}
120
121let foo: Foo = xso::from_bytes(b"<a xmlns='urn:example' foo='hello'/>").unwrap();
122assert_eq!(foo, Foo::Variant1 { foo: "hello".to_string() });
123
124let foo: Foo = xso::from_bytes(b"<b xmlns='urn:example' bar='hello'/>").unwrap();
125assert_eq!(foo, Foo::Variant2 { bar: "hello".to_string() });
126```
127
128### Field meta
129
130For fields, the *meta* consists of a nested meta inside the `#[xml(..)]` meta,
131the identifier of which controls *how* the field is mapped to XML, while the
132contents control the parameters of that mapping.
133
134The following mapping types are defined:
135
136| Type | Description |
137| --- | --- |
138| [`attribute`](#attribute-meta) | Map the field to an XML attribute on the struct's element |
139| [`child`](#child-meta) | Map the field to a child element |
140| [`text`](#text-meta) | Map the field to the text content of the struct's element |
141
142#### `attribute` meta
143
144The `attribute` meta causes the field to be mapped to an XML attribute of the
145same name. For `FromXml`, the field's type must implement [`FromXmlText`] and
146for `AsXml`, the field's type must implement [`AsOptionalXmlText`].
147
148The following keys can be used inside the `#[xml(attribute(..))]` meta:
149
150| Key | Value type | Description |
151| --- | --- | --- |
152| `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. |
153| `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`. |
154| `default` | flag | If present, an absent attribute will substitute the default value instead of raising an error. |
155
156If the `name` key contains a namespace prefix, it must be one of the prefixes
157defined as built-in in the XML specifications. That prefix will then be
158expanded to the corresponding namespace URI and the value for the `namespace`
159key is implied. Mixing a prefixed name with an explicit `namespace` key is
160not allowed.
161
162The `attribute` meta also supports a shorthand syntax,
163`#[xml(attribute = ..)]`, where the value is treated as the value for the
164`name` key (with optional prefix as described above, and unnamespaced
165otherwise).
166
167If `default` is specified and the attribute is absent in the source, the value
168is generated using [`std::default::Default`], requiring the field type to
169implement the `Default` trait for a `FromXml` derivation. `default` has no
170influence on `AsXml`.
171
172##### Example
173
174```rust
175# use xso::FromXml;
176#[derive(FromXml, Debug, PartialEq)]
177#[xml(namespace = "urn:example", name = "foo")]
178struct Foo {
179 #[xml(attribute)]
180 a: String,
181 #[xml(attribute = "bar")]
182 b: String,
183 #[xml(attribute(name = "baz"))]
184 c: String,
185 #[xml(attribute(namespace = "urn:example", name = "fnord"))]
186 d: String,
187 #[xml(attribute = "xml:lang")]
188 e: String,
189};
190
191let foo: Foo = xso::from_bytes(b"<foo
192 xmlns='urn:example'
193 a='1' bar='2' baz='3'
194 xmlns:tns0='urn:example' tns0:fnord='4'
195 xml:lang='5'
196/>").unwrap();
197assert_eq!(foo, Foo {
198 a: "1".to_string(),
199 b: "2".to_string(),
200 c: "3".to_string(),
201 d: "4".to_string(),
202 e: "5".to_string(),
203});
204```
205
206#### `child` meta
207
208The `child` meta causes the field to be mapped to a child element of the
209element.
210
211| Key | Value type | Description |
212| --- | --- | --- |
213| `default` | flag | If present, an absent child will substitute the default value instead of raising an error. |
214
215The field's type must implement [`FromXml`] in order to derive `FromXml` and
216[`AsXml`] in order to derive `AsXml`.
217
218If `default` is specified and the child is absent in the source, the value
219is generated using [`std::default::Default`], requiring the field type to
220implement the `Default` trait for a `FromXml` derivation. `default` has no
221influence on `AsXml`.
222
223##### Example
224
225```rust
226# use xso::FromXml;
227#[derive(FromXml, Debug, PartialEq)]
228#[xml(namespace = "urn:example", name = "child")]
229struct Child {
230 #[xml(attribute = "some-attr")]
231 some_attr: String,
232}
233
234#[derive(FromXml, Debug, PartialEq)]
235#[xml(namespace = "urn:example", name = "parent")]
236struct Parent {
237 #[xml(attribute)]
238 foo: String,
239
240 #[xml(child)]
241 bar: Child,
242}
243
244let parent: Parent = xso::from_bytes(b"<parent
245 xmlns='urn:example'
246 foo='hello world!'
247><child some-attr='within'/></parent>").unwrap();
248assert_eq!(parent, Parent {
249 foo: "hello world!".to_owned(),
250 bar: Child { some_attr: "within".to_owned() },
251});
252```
253
254#### `text` meta
255
256The `text` meta causes the field to be mapped to the text content of the
257element.
258
259| Key | Value type | Description |
260| --- | --- | --- |
261| `codec` | *type* | Optional [`TextCodec`] implementation which is used to encode or decode the field. |
262
263If `codec` is given, the given `codec` must implement
264[`TextCodec<T>`][`TextCodec`] where `T` is the type of the field.
265
266If `codec` is *not* given, the field's type must implement [`FromXmlText`] for
267`FromXml` and for `AsXml`, the field's type must implement [`AsXmlText`].
268
269The `text` meta also supports a shorthand syntax, `#[xml(text = ..)]`, where
270the value is treated as the value for the `codec` key (with optional prefix as
271described above, and unnamespaced otherwise).
272
273Only a single field per struct may be annotated with `#[xml(text)]` at a time,
274to avoid parsing ambiguities. This is also true if only `AsXml` is derived on
275a field, for consistency.
276
277##### Example without codec
278
279```rust
280# use xso::FromXml;
281#[derive(FromXml, Debug, PartialEq)]
282#[xml(namespace = "urn:example", name = "foo")]
283struct Foo {
284 #[xml(text)]
285 a: String,
286};
287
288let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'>hello</foo>").unwrap();
289assert_eq!(foo, Foo {
290 a: "hello".to_string(),
291});
292```
293
294##### Example with codec
295
296```rust
297# use xso::FromXml;
298#[derive(FromXml, Debug, PartialEq)]
299#[xml(namespace = "urn:example", name = "foo")]
300struct Foo {
301 #[xml(text = xso::text::EmptyAsNone)]
302 a: Option<String>,
303};
304
305let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'/>").unwrap();
306assert_eq!(foo, Foo {
307 a: None,
308});
309```