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## Table of contents
19
201. [Attributes](#attributes)
212. [Struct meta](#struct-meta)
223. [Enums](#enums)
23 1. [Name-switched enum meta](#name-switched-enum-meta)
24 2. [Attribute-switched enum meta](#attribute-switched-enum-meta)
25 3. [Dynamic enum meta](#dynamic-enum-meta)
264. [Field meta](#field-meta)
27 1. [`attribute` meta](#attribute-meta)
28 2. [`child` meta](#child-meta)
29 3. [`element` meta](#element-meta)
30 4. [`extract` meta](#extract-meta)
31 5. [`flag` meta](#flag-meta)
32 6. [`lang` meta](#lang-meta)
33 6. [`text` meta](#text-meta)
34
35## Attributes
36
37The derive macros need additional information, such as XML namespaces and
38names to match. This must be specified via key-value pairs on the type or
39fields the derive macro is invoked on. These key-value pairs are specified as
40Rust attributes. In order to disambiguate between XML attributes and Rust
41attributes, we are going to refer to Rust attributes using the term *meta*
42instead, which is consistent with the Rust language reference calling that
43syntax construct *meta*.
44
45All key-value pairs interpreted by these derive macros must be wrapped in a
46`#[xml( ... )]` *meta*.
47
48The values associated with the keys may be of different types, defined as
49such:
50
51- *path*: A Rust path, like `some_crate::foo::Bar`. Note that `foo` on its own
52 is also a path.
53- *string literal*: A string literal, like `"hello world!"`.
54- *type*: A Rust type.
55- *expression*: A Rust expression.
56- *ident*: A single Rust identifier.
57- *nested*: The meta is followed by parentheses, inside of which meta-specific
58 additional keys are present.
59- flag: Has no value. The key's mere presence has relevance and it must not be
60 followed by a `=` sign.
61
62## Struct meta
63
64The following keys are defined on structs:
65
66| Key | Value type | Description |
67| --- | --- | --- |
68| `namespace` | *string literal* or *path* | The XML element namespace to match. If it is a *path*, it must point at a `&'static str`. |
69| `name` | *string literal* or *path* | The XML element name to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
70| `transparent` | *flag* | If present, declares the struct as *transparent* struct (see below) |
71| `builder` | optional *ident* | The name to use for the generated builder type. |
72| `iterator` | optional *ident* | The name to use for the generated iterator type. |
73| `on_unknown_attribute` | optional *ident* | Name of an [`UnknownAttributePolicy`] member, controlling how unknown attributes are handled. |
74| `on_unknown_child` | optional *ident* | Name of an [`UnknownChildPolicy`] member, controlling how unknown children are handled. |
75| `discard` | optional *nested* | Contains field specifications of content to ignore. See below for details. |
76| `deserialize_callback` | optional *path* | Path to a `fn(&mut T) -> Result<(), Error>` which is called on the deserialized struct after deserialization. |
77
78Note that the `name` value must be a valid XML element name, without colons.
79The namespace prefix, if any, is assigned automatically at serialisation time
80and cannot be overridden. The following will thus not compile:
81
82```compile_fail
83# use xso::FromXml;
84#[derive(FromXml, Debug, PartialEq)]
85#[xml(namespace = "urn:example", name = "fnord:foo")] // colon not allowed
86struct Foo;
87```
88
89If `builder` or `iterator` are given, the respective generated types will use
90the given names instead of names chosen by the derive macro implementation.
91Helper types will use these names as prefix. The exact names of helper types
92are implementation defined, which is why any type name starting with the
93identifiers passed to either of these keys is considered reserved.
94
95By default, the builder type uses the type's name suffixed with
96`FromXmlBuilder` and the iterator type uses the type's name suffixed with
97`AsXmlIterator`.
98
99If the struct is marked as `transparent`, it must not have a `namespace` or
100`name` set and it must have exactly one field. That field's type must
101implement [`FromXml`] in order to derive `FromXml` and [`AsXml`] in order to
102derive `AsXml`. The struct will be (de-)serialised exactly like the type of
103that single field. This allows a newtype-like pattern for XSO structs.
104
105`discard` may contain zero or more field meta which describe XML content to
106silently ignore. The syntax is the same as within the `#[xml(..)]` meta used
107on fields, however, any parameters which aren't strictly needed to match the
108content are rejected (for example, you cannot set the codec on a discarded
109attribute because it is irrelevant). Discarded content is never emitted during
110serialisation. Its absence does not cause errors.
111
112```
113# use xso::FromXml;
114#[derive(FromXml, Debug, PartialEq)]
115#[xml(namespace = "urn:example", name = "foo", discard(text))]
116struct Foo;
117```
118
119## Enums
120
121Two different `enum` flavors are supported:
122
1231. [**Name-switched enums**](#name-switched-enum-meta) have a fixed XML
124 namespace they match on and each variant corresponds to a different XML
125 element name within that namespace.
126
1272. [**Attribute-switched enums**](#attribute-switched-enum-meta) have a fixed
128 XML element they match which must have a specific attribute. The variants
129 correspond to a value of that XML attribute.
130
1313. [**Dynamic enums**](#dynamic-enum-meta) have entirely unrelated variants.
132
133At the source-code level, they are distinguished by the meta keys which are
134present on the `enum`: The different variants have different sets of mandatory
135keys and can thus be uniquely identified.
136
137### Name-switched enum meta
138
139Name-switched enums match a fixed XML namespace and then select the enum
140variant based on the XML element's name. Name-switched enums are declared by
141setting the `namespace` key on a `enum` item.
142
143The following keys are defined on name-switched enums:
144
145| Key | Value type | Description |
146| --- | --- | --- |
147| `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`. |
148| `builder` | optional *ident* | The name to use for the generated builder type. |
149| `iterator` | optional *ident* | The name to use for the generated iterator type. |
150| `exhaustive` | *flag* | If present, the enum considers itself authoritative for its namespace; unknown elements within the namespace are rejected instead of treated as mismatch. |
151| `discard` | optional *nested* | Contains field specifications of content to ignore. See the struct meta docs for details. |
152| `deserialize_callback` | optional *path* | Path to a `fn(&mut T) -> Result<(), Error>` which is called on the deserialized enum after deserialization. |
153
154All variants of a name-switched enum live within the same namespace and are
155distinguished exclusively by their XML name within that namespace. The
156contents of the XML element (including attributes) is not inspected before
157selecting the variant when parsing XML.
158
159If *exhaustive* is set and an element is encountered which matches the
160namespace of the enum, but matches none of its variants, parsing will fail
161with an error. If *exhaustive* is *not* set, in such a situation, parsing
162would attempt to continue with other siblings of the enum, attempting to find
163a handler for that element.
164
165Note that the *exhaustive* flag is orthogonal to the Rust attribute
166`#[non_exhaustive]`.
167
168For details on `builder` and `iterator`, see the [Struct meta](#struct-meta)
169documentation above.
170
171#### Name-switched enum variant meta
172
173| Key | Value type | Description |
174| --- | --- | --- |
175| `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`. |
176| `on_unknown_attribute` | optional *ident* | Name of an [`UnknownAttributePolicy`] member, controlling how unknown attributes are handled. |
177| `on_unknown_child` | optional *ident* | Name of an [`UnknownChildPolicy`] member, controlling how unknown children are handled. |
178
179Note that the `name` value must be a valid XML element name, without colons.
180The namespace prefix, if any, is assigned automatically at serialisation time
181and cannot be overridden.
182
183#### Example
184
185```rust
186# use xso::FromXml;
187#[derive(FromXml, Debug, PartialEq)]
188#[xml(namespace = "urn:example")]
189enum Foo {
190 #[xml(name = "a")]
191 Variant1 {
192 #[xml(attribute)]
193 foo: String,
194 },
195 #[xml(name = "b")]
196 Variant2 {
197 #[xml(attribute)]
198 bar: String,
199 },
200}
201
202let foo: Foo = xso::from_bytes(b"<a xmlns='urn:example' foo='hello'/>").unwrap();
203assert_eq!(foo, Foo::Variant1 { foo: "hello".to_string() });
204
205let foo: Foo = xso::from_bytes(b"<b xmlns='urn:example' bar='hello'/>").unwrap();
206assert_eq!(foo, Foo::Variant2 { bar: "hello".to_string() });
207```
208
209### Attribute-switched enum meta
210
211Attribute-switched enums match a fixed XML element and then select the enum
212variant based on a specific attribute on that XML element. Attribute-switched
213enums are declared by setting the `namespace`, `name` and `attribute` keys on
214a `enum` item.
215
216The following keys are defined on name-switched enums:
217
218| Key | Value type | Description |
219| --- | --- | --- |
220| `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`. |
221| `name` | *string literal* or *path* | The XML element name to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
222| `attribute` | *string literal*, *path* or *nested* | The attribute to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
223| `builder` | optional *ident* | The name to use for the generated builder type. |
224| `iterator` | optional *ident* | The name to use for the generated iterator type. |
225| `exhaustive` | *flag* | Must be set to allow future extensions. |
226| `discard` | optional *nested* | Contains field specifications of content to ignore. See the struct meta docs for details. |
227| `deserialize_callback` | optional *path* | Path to a `fn(&mut T) -> Result<(), Error>` which is called on the deserialized enum after deserialization. |
228
229`attribute` follows the same syntax and semantic as the
230[`attribute` meta](#attribute-meta), but only allows the `namespace` and
231`name` keys.
232
233For details on `builder` and `iterator`, see the [Struct meta](#struct-meta)
234documentation above.
235
236#### Attribute-switched enum variant meta
237
238| Key | Value type | Description |
239| --- | --- | --- |
240| `value` | *string literal* | The text content to match for this variant. |
241| `on_unknown_attribute` | optional *ident* | Name of an [`UnknownAttributePolicy`] member, controlling how unknown attributes are handled. |
242| `on_unknown_child` | optional *ident* | Name of an [`UnknownChildPolicy`] member, controlling how unknown children are handled. |
243
244#### Example
245
246```rust
247# use xso::FromXml;
248#[derive(FromXml, Debug, PartialEq)]
249#[xml(namespace = "urn:example", name = "foo", attribute = "version", exhaustive)]
250enum Foo {
251 #[xml(value = "a")]
252 Variant1 {
253 #[xml(attribute)]
254 foo: String,
255 },
256 #[xml(value = "b")]
257 Variant2 {
258 #[xml(attribute)]
259 bar: String,
260 },
261}
262
263let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example' version='a' foo='hello'/>").unwrap();
264assert_eq!(foo, Foo::Variant1 { foo: "hello".to_string() });
265
266let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example' version='b' bar='hello'/>").unwrap();
267assert_eq!(foo, Foo::Variant2 { bar: "hello".to_string() });
268```
269
270### Dynamic enum meta
271
272Dynamic enums select their variants by attempting to match them in declaration
273order. Dynamic enums are declared by not setting the `namespace` key on an
274`enum` item.
275
276The following keys are defined on dynamic enums:
277
278| Key | Value type | Description |
279| --- | --- | --- |
280| `builder` | optional *ident* | The name to use for the generated builder type. |
281| `iterator` | optional *ident* | The name to use for the generated iterator type. |
282| `discard` | optional *nested* | Contains field specifications of content to ignore. See the struct meta docs for details. |
283| `deserialize_callback` | optional *path* | Path to a `fn(&mut T) -> Result<(), Error>` which is called on the deserialized enum after deserialization. |
284
285For details on `builder` and `iterator`, see the [Struct meta](#struct-meta)
286documentation above.
287
288#### Dynamic enum variant meta
289
290Dynamic enum variants are completely independent of one another and thus use
291the same meta structure as structs. See [Struct meta](#struct-meta) for
292details.
293
294The `builder`, `iterator` and `debug` keys cannot be used on dynamic enum
295variants.
296
297#### Example
298
299```rust
300# use xso::FromXml;
301#[derive(FromXml, Debug, PartialEq)]
302#[xml()]
303enum Foo {
304 #[xml(namespace = "urn:example:ns1", name = "a")]
305 Variant1 {
306 #[xml(attribute)]
307 foo: String,
308 },
309 #[xml(namespace = "urn:example:ns2", name = "b")]
310 Variant2 {
311 #[xml(attribute)]
312 bar: String,
313 },
314}
315
316let foo: Foo = xso::from_bytes(b"<a xmlns='urn:example:ns1' foo='hello'/>").unwrap();
317assert_eq!(foo, Foo::Variant1 { foo: "hello".to_string() });
318
319let foo: Foo = xso::from_bytes(b"<b xmlns='urn:example:ns2' bar='hello'/>").unwrap();
320assert_eq!(foo, Foo::Variant2 { bar: "hello".to_string() });
321```
322
323## Field meta
324
325For fields, the *meta* consists of a nested meta inside the `#[xml(..)]` meta,
326the identifier of which controls *how* the field is mapped to XML, while the
327contents control the parameters of that mapping.
328
329The following mapping types are defined:
330
331| Type | Description |
332| --- | --- |
333| [`attribute`](#attribute-meta) | Map the field to an XML attribute on the struct's element |
334| [`child`](#child-meta) | Map the field to a child element |
335| [`element`](#element-meta) | Map the field to a child element as [`minidom::Element`] |
336| [`extract`](#extract-meta) | Map the field to contents of a child element of specified structure |
337| [`text`](#text-meta) | Map the field to the text content of the struct's element |
338
339#### Field order
340
341Field order **matters**. The fields are parsed in the order they are declared
342(for children, anyway). If multiple fields match a given child element, the
343first field which matches will be taken. The only exception is
344`#[xml(element(n = ..))]`, which is always processed last.
345
346When XML is generated from a struct, the child elements are also generated
347in the order of the fields. That means that passing an XML element through
348`FromXml` and `AsXml` may re-order some child elements.
349
350Sorting order between elements which match the same field is generally
351preserved, if the container preserves sort order on insertion.
352
353### `attribute` meta
354
355The `attribute` meta causes the field to be mapped to an XML attribute of the
356same name. For `FromXml`, the field's type must implement [`FromXmlText`] and
357for `AsXml`, the field's type must implement [`AsOptionalXmlText`].
358
359The following keys can be used inside the `#[xml(attribute(..))]` meta:
360
361| Key | Value type | Description |
362| --- | --- | --- |
363| `namespace` | optional *string literal* or *path* | The 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. |
364| `name` | optional *string literal* or *path* | The name of the XML attribute to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
365| `default` | *flag* | If present, an absent attribute will substitute the default value instead of raising an error. |
366| `type_` | *type* | Optional explicit type specification. Only allowed within `#[xml(extract(fields(..)))]`. |
367| `codec` | optional *expression* | [`TextCodec`] implementation which is used to encode or decode the field. |
368
369If the `name` key contains a namespace prefix, it must be one of the prefixes
370defined as built-in in the XML specifications. That prefix will then be
371expanded to the corresponding namespace URI and the value for the `namespace`
372key is implied. Mixing a prefixed name with an explicit `namespace` key is
373not allowed.
374
375The `attribute` meta also supports a shorthand syntax,
376`#[xml(attribute = ..)]`, where the value is treated as the value for the
377`name` key (with optional prefix as described above, and unnamespaced
378otherwise).
379
380If `default` is specified and the attribute is absent in the source, the value
381is generated using [`core::default::Default`], requiring the field type to
382implement the `Default` trait for a `FromXml` derivation. `default` has no
383influence on `AsXml`.
384
385If `type_` is specified and the `attribute` meta is used within an
386`#[xml(extract(fields(..)))]` meta, the specified type is used instead of the
387field type on which the `extract` is declared.
388
389If `codec` is given, the given `codec` value must implement
390[`TextCodec<T>`][`TextCodec`] where `T` is the type of the field.
391
392#### Example
393
394```rust
395# use xso::FromXml;
396#[derive(FromXml, Debug, PartialEq)]
397#[xml(namespace = "urn:example", name = "foo")]
398struct Foo {
399 #[xml(attribute)]
400 a: String,
401 #[xml(attribute = "bar")]
402 b: String,
403 #[xml(attribute(name = "baz"))]
404 c: String,
405 #[xml(attribute(namespace = "urn:example", name = "fnord"))]
406 d: String,
407 #[xml(attribute = "xml:lang")]
408 e: String,
409};
410
411let foo: Foo = xso::from_bytes(b"<foo
412 xmlns='urn:example'
413 a='1' bar='2' baz='3'
414 xmlns:tns0='urn:example' tns0:fnord='4'
415 xml:lang='5'
416/>").unwrap();
417assert_eq!(foo, Foo {
418 a: "1".to_string(),
419 b: "2".to_string(),
420 c: "3".to_string(),
421 d: "4".to_string(),
422 e: "5".to_string(),
423});
424```
425
426Note that it is not possible to have two `#[xml(attribute)]` fields which
427match the same XML attribute:
428
429```compile_fail
430# use xso::FromXml;
431#[derive(FromXml)]
432#[xml(namespace = "urn:example", name = "dup")]
433struct Dup {
434 #[xml(attribute)]
435 a: String,
436
437 #[xml(attribute = "a")]
438 b: String,
439}
440```
441
442```compile_fail
443# use xso::FromXml;
444static A: &str = "a";
445
446#[derive(FromXml)]
447#[xml(namespace = "urn:example", name = "dup")]
448struct Dup {
449 #[xml(attribute)]
450 a: String,
451
452 #[xml(attribute = A)]
453 b: String,
454}
455```
456
457### `child` meta
458
459The `child` meta causes the field to be mapped to a child element of the
460element.
461
462The following keys can be used inside the `#[xml(child(..))]` meta:
463
464| Key | Value type | Description |
465| --- | --- | --- |
466| `default` | *flag* | If present, an absent child will substitute the default value instead of raising an error. |
467| `n` | `1` or `..` | If `1`, a single element is parsed. If `..`, a collection is parsed. Defaults to `1`. |
468
469When parsing a single child element (i.e. `n = 1` or no `n` value set at all),
470the field's type must implement [`FromXml`] in order to derive `FromXml` and
471[`AsXml`] in order to derive `AsXml`.
472
473When parsing a collection (with `n = ..`), the field's type must implement
474[`IntoIterator<Item = T>`][`core::iter::IntoIterator`], where `T` must
475implement [`FromXml`] in order to derive `FromXml` and [`AsXml`] in order to
476derive `AsXml`. In addition, the field's type must implement
477[`Extend<T>`][`core::iter::Extend`] to derive `FromXml` and the field's
478reference type must implement `IntoIterator<Item = &'_ T>` to derive `AsXml`.
479
480If `default` is specified and the child is absent in the source, the value
481is generated using [`core::default::Default`], requiring the field type to
482implement the `Default` trait for a `FromXml` derivation. `default` has no
483influence on `AsXml`. Combining `default` and `n` where `n` is not set to `1`
484is not supported and will cause a compile-time error.
485
486Using `default` with a type other than `Option<T>` will cause the
487serialisation to mismatch the deserialisation (i.e. the struct is then not
488roundtrip-safe), because the deserialisation does not compare the value
489against `default` (but has special provisions to work with `Option<T>`).
490
491#### Example
492
493```rust
494# use xso::FromXml;
495#[derive(FromXml, Debug, PartialEq)]
496#[xml(namespace = "urn:example", name = "child")]
497struct Child {
498 #[xml(attribute = "some-attr")]
499 some_attr: String,
500}
501
502#[derive(FromXml, Debug, PartialEq)]
503#[xml(namespace = "urn:example", name = "other-child")]
504struct OtherChild {
505 #[xml(attribute = "some-attr")]
506 some_attr: String,
507}
508
509#[derive(FromXml, Debug, PartialEq)]
510#[xml(namespace = "urn:example", name = "parent")]
511struct Parent {
512 #[xml(attribute)]
513 foo: String,
514
515 #[xml(child)]
516 bar: Child,
517
518 #[xml(child(n = ..))]
519 baz: Vec<OtherChild>,
520}
521
522let parent: Parent = xso::from_bytes(b"<parent
523 xmlns='urn:example'
524 foo='hello world!'
525><child
526 some-attr='within'
527/><other-child
528 some-attr='c1'
529/><other-child
530 some-attr='c2'
531/></parent>").unwrap();
532assert_eq!(parent, Parent {
533 foo: "hello world!".to_owned(),
534 bar: Child { some_attr: "within".to_owned() },
535 baz: vec! [
536 OtherChild { some_attr: "c1".to_owned() },
537 OtherChild { some_attr: "c2".to_owned() },
538 ],
539});
540```
541
542### `element` meta
543
544The `element` meta causes the field to be mapped to child elements, stored as
545a container containing [`minidom::Element`] instances.
546
547This meta is only available if `xso` is being built with the `"minidom"`
548feature.
549
550The following keys can be used inside the `#[xml(extract(..))]` meta:
551
552| Key | Value type | Description |
553| --- | --- | --- |
554| `default` | flag | If present, an absent child will substitute the default value instead of raising an error. |
555| `n` | `1` or `..` | If `1`, a single element is parsed. If `..`, a collection is parsed. Defaults to `1`. |
556
557When parsing a single child element (i.e. `n = 1` or no `n` value set at all),
558the field's type must be a `minidom::Element`.
559
560When parsing a collection (with `n = ..`), the field's type must be a
561collection of `minidom::Element`. It must thus implement
562[`IntoIterator<Item = minidom::Element>`][`core::iter::IntoIterator`]. In
563addition, the field's type must implement
564[`Extend<minidom::Element>`][`core::iter::Extend`] to derive `FromXml` and the
565field's reference type must implement
566`IntoIterator<Item = &'_ minidom::Element>` to derive `AsXml`.
567
568If `default` is specified and the child is absent in the source, the value
569is generated using [`core::default::Default`]. `default` has no influence on
570`AsXml`. Combining `default` and `n` where `n` is not set to `1` is not
571supported and will cause a compile-time error.
572
573Using `default` with a type other than `Option<T>` will cause the
574serialisation to mismatch the deserialisation (i.e. the struct is then not
575roundtrip-safe), because the deserialisation does not compare the value
576against `default` (but has special provisions to work with `Option<T>`).
577
578Fields with the `element` meta are deserialised with the lowest priority.
579While other fields are processed in the order they are declared, `element`
580fields may capture arbitrary child elements, so they are considered as the
581last choice when no other field matched a given child element. In addition,
582it is not allowed to have more than one field in any given struct with the
583`#[xml(element)]` meta.
584
585#### Example
586
587```rust
588# #[cfg(feature = "minidom")]
589# {
590# use xso::FromXml;
591# use xso::exports::minidom;
592#[derive(FromXml, Debug, PartialEq)]
593#[xml(namespace = "urn:example", name = "parent")]
594struct Parent {
595 #[xml(element(n = ..))]
596 misc: Vec<minidom::Element>,
597}
598
599let parent: Parent = xso::from_bytes(b"<parent
600 xmlns='urn:example'
601><child-a/><child-b/><child-a/></parent>").unwrap();
602assert_eq!(parent.misc[0].name(), "child-a");
603assert_eq!(parent.misc[1].name(), "child-b");
604assert_eq!(parent.misc[2].name(), "child-a");
605# }
606```
607
608### `extract` meta
609
610The `extract` meta causes the field to be mapped to the *contents* of a child
611element.
612
613The following keys can be used inside the `#[xml(extract(..))]` meta:
614
615| Key | Value type | Description |
616| --- | --- | --- |
617| `namespace` | optional *string literal* or *path* | The XML namespace of the child element. |
618| `name` | optional *string literal* or *path* | The XML name of the child element. If it is a *path*, it must point at a `&'static NcNameStr`. |
619| `default` | *flag* | If present, an absent child will substitute the default value instead of raising an error. |
620| `n` | `1` or `..` | If `1`, a single element is parsed. If `..`, a collection is parsed. Defaults to `1`. |
621| `fields` | *nested* | A list of [field meta](#field-meta) which describe the contents of the child element. |
622| `on_unknown_attribute` | optional *ident* | Name of an [`UnknownAttributePolicy`] member, controlling how unknown attributes are handled. |
623| `on_unknown_child` | optional *ident* | Name of an [`UnknownChildPolicy`] member, controlling how unknown children are handled. |
624
625If the `name` key contains a namespace prefix, it must be one of the prefixes
626defined as built-in in the XML specifications. That prefix will then be
627expanded to the corresponding namespace URI and the value for the `namespace`
628key is implied. Mixing a prefixed name with an explicit `namespace` key is
629not allowed.
630
631Both `namespace` and `name` may be omitted. If `namespace` is omitted, it
632defaults to the namespace of the surrounding container. If `name` is omitted
633and the `extract` meta is being used on a named field, that field's name is
634used. If `name` is omitted and `extract` is not used on a named field, an
635error is emitted.
636
637When parsing a single child element (i.e. `n = 1` or no `n` value set at all),
638the extracted field's type is set to be the same type as the field on which
639the extract is declared, unless overridden in the extracted field's meta.
640
641When parsing a collection (with `n = ..`), the extracted fields within
642`fields()` must all have type specifications. Not all fields kinds support
643that.
644
645The sequence of field meta inside `fields` can be thought of as a nameless
646tuple-style struct. The macro generates serialisation/deserialisation code
647for that nameless tuple-style struct and uses it to serialise/deserialise
648the field.
649
650If `default` is specified and the child is absent in the source, the value
651is generated using [`core::default::Default`], requiring the field type to
652implement the `Default` trait for a `FromXml` derivation. `default` has no
653influence on `AsXml`. Combining `default` and `n` where `n` is not set to `1`
654is not supported and will cause a compile-time error.
655
656Mixing `default` on the `#[xml(extract)]` itself with `default` on the
657extracted inner fields creates non-roundtrip-safe parsing, unless you also
658use twice-nested [`core::option::Option`] types. That means that when
659deserialising a piece of XML and reserialising it without changing the
660contents of the struct in Rust, the resulting XML may not match the input.
661This is because to the serialiser, if only one layer of
662[`core::option::Option`] is used, it is not possible to distinguish which of
663the two layers were defaulted. The exact behaviour on serialisation in such a
664situation is *not guaranteed* and may change between versions of the `xso`
665crate, its dependencies, the standard library, or even rustc itself.
666
667Using `default` with a type other than `Option<T>` will cause the
668serialisation to mismatch the deserialisation, too (i.e. the struct is then
669not roundtrip-safe), because the deserialisation does not compare the value
670against `default` (but has special provisions to work with `Option<T>`).
671
672If more than one single field is contained in `fields`, the fields will be
673extracted as a tuple in the order they are given in the meta. In addition, it
674is required to explicitly specify each extracted field's type in that case.
675
676Using `extract` instead of `child` combined with a specific struct declaration
677comes with trade-offs. On the one hand, using `extract` gives you flexibility
678in regard of the specific serialisation of a field: it is possible to exchange
679a nested child element for an attribute without changing the Rust interface
680of the struct.
681
682On the other hand, `extract` meta declarations can quickly become unwieldy
683and they may not support all configuration options which may in the future be
684added on structs (such as configuring handling of undeclared attributes) and
685they cannot be used for enumerations.
686
687#### Example
688
689```rust
690# use xso::FromXml;
691#[derive(FromXml, Debug, PartialEq)]
692#[xml(namespace = "urn:example", name = "foo")]
693struct Foo {
694 #[xml(extract(namespace = "urn:example", name = "bar", fields(attribute = "a")))]
695 a: String,
696}
697
698let foo: Foo = xso::from_bytes(b"<foo
699 xmlns='urn:example'><bar a='xyz'/></foo>").unwrap();
700assert_eq!(foo, Foo {
701 a: "xyz".to_string(),
702});
703```
704
705### `flag` meta
706
707The `flag` meta causes the field to be mapped to a single, optional child element. Absence of the child is equivalent to the value `false`, presence
708of the child element is equivalent to the value `true`.
709
710The following keys can be used inside the `#[xml(flag(..))]` meta:
711
712| Key | Value type | Description |
713| --- | --- | --- |
714| `namespace` | optional *string literal* or *path* | The namespace of the XML element to match. If it is a *path*, it must point at a `&'static str`. |
715| `name` | optional *string literal* or *path* | The name of the XML element to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
716
717The field on which the `flag` meta is used must be of type `bool`.
718
719Both `namespace` and `name` may be omitted. If `namespace` is omitted, it
720defaults to the namespace of the surrounding container. If `name` is omitted
721and the `flag` meta is being used on a named field, that field's name is
722used. If `name` is omitted and `flag` is not used on a named field, an
723error is emitted.
724
725When parsing, any contents within the child element generate a parse error.
726
727#### Example
728
729```rust
730# use xso::FromXml;
731#[derive(FromXml, Debug, PartialEq)]
732#[xml(namespace = "urn:example", name = "foo")]
733struct Foo {
734 #[xml(flag(name = "flag"))]
735 flag: bool,
736};
737
738let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'><flag/></foo>").unwrap();
739assert_eq!(foo, Foo {
740 flag: true,
741});
742
743let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'/>").unwrap();
744assert_eq!(foo, Foo {
745 flag: false,
746});
747```
748
749### `lang` meta
750
751The `lang` meta allows to access the (potentially inherited) logical
752`xml:lang` value as defined in
753[XML 1.0 § 2.12](https://www.w3.org/TR/REC-xml/#sec-lang-tag). For `FromXml`,
754the field's type must implement [`FromXmlText`] and for `AsXml`, the field's
755type must implement [`AsOptionalXmlText`].
756
757| Key | Value type | Description |
758| --- | --- | --- |
759| `default` | *flag* | If present, an absent attribute will substitute the default value instead of raising an error. |
760| `type_` | *type* | Optional explicit type specification. Only allowed within `#[xml(extract(fields(..)))]`. |
761| `codec` | optional *expression* | [`TextCodec`] implementation which is used to encode or decode the field. |
762
763Unlike `#[xml(attribute = "xml:lang")]`, using `#[xml(lang)]` takes
764the inheritance of the `xml:lang` attribute into account.
765
766**Note:** Using this meta is not roundtrip-safe. `xso` will always emit its
767value on serialisation, even if it was inherited during deserialisation.
768
769If `default` is specified and there is no `xml:lang` specified at the point of
770the element, the value is generated using [`core::default::Default`],
771requiring the field type to implement the `Default` trait for a `FromXml`
772derivation. `default` has no influence on `AsXml`. If `default` is not
773specified, an error is raised if `xml:lang` has not been set on the element
774or any of its ancestors.
775
776Note that no error is generated (by `xso`) for `xml:lang` values of `""`.
777
778If `type_` is specified and the `lang` meta is used within an
779`#[xml(extract(fields(..)))]` meta, the specified type is used instead of the
780field type on which the `extract` is declared.
781
782If `codec` is given, the given `codec` value must implement
783[`TextCodec<T>`][`TextCodec`] where `T` is the type of the field.
784
785#### Example
786
787```rust
788# use xso::FromXml;
789#[derive(FromXml, Debug, PartialEq)]
790#[xml(namespace = "urn:example", name = "bar")]
791struct Bar {
792 #[xml(lang)]
793 lang: Option<String>,
794};
795
796#[derive(FromXml, Debug, PartialEq)]
797#[xml(namespace = "urn:example", name = "foo")]
798struct Foo {
799 #[xml(child)]
800 child: Bar,
801};
802
803// `xml:lang` gets inherited from <foo/> to <bar/>
804let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example' xml:lang='en'><bar/></foo>").unwrap();
805assert_eq!(foo, Foo {
806 child: Bar {
807 lang: Some("en".to_owned()),
808 },
809});
810
811// `xml:lang` gets set/overwritten in <bar/>
812let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example' xml:lang='en'><bar xml:lang='de'/></foo>").unwrap();
813assert_eq!(foo, Foo {
814 child: Bar {
815 lang: Some("de".to_owned()),
816 },
817});
818```
819
820Note that it is not possible to use `#[xml(lang)]` and an `#[xml(attribute)]`
821which also matches `xml:lang` in the same struct:
822
823```compile_fail
824# use xso::FromXml;
825# use xso::exports::rxml::XMLNS_XML;
826#[derive(FromXml)]
827#[xml(namespace = "urn:example", name = "dup")]
828struct Dup {
829 #[xml(attribute(namespace = XMLNS_XML, name = "lang"))]
830 a: String,
831
832 #[xml(lang)]
833 b: Option<String>,
834}
835```
836
837### `text` meta
838
839The `text` meta causes the field to be mapped to the text content of the
840element.
841
842| Key | Value type | Description |
843| --- | --- | --- |
844| `codec` | optional *expression* | [`TextCodec`] implementation which is used to encode or decode the field. |
845| `type_` | optional *type* | Explicit type specification. Only allowed within `#[xml(extract(fields(..)))]`. |
846
847If `codec` is given, the given `codec` value must implement
848[`TextCodec<T>`][`TextCodec`] where `T` is the type of the field.
849
850If `codec` is *not* given, the field's type must implement [`FromXmlText`] for
851`FromXml` and for `AsXml`, the field's type must implement [`AsXmlText`].
852
853If `type_` is specified and the `text` meta is used within an
854`#[xml(extract(fields(..)))]` meta, the specified type is used instead of the
855field type on which the `extract` is declared.
856
857The `text` meta also supports a shorthand syntax, `#[xml(text = ..)]`, where
858the value is treated as the value for the `codec` key (with optional prefix as
859described above, and unnamespaced otherwise).
860
861Only a single field per struct may be annotated with `#[xml(text)]` at a time,
862to avoid parsing ambiguities. This is also true if only `AsXml` is derived on
863a field, for consistency.
864
865#### Example without codec
866
867```rust
868# use xso::FromXml;
869#[derive(FromXml, Debug, PartialEq)]
870#[xml(namespace = "urn:example", name = "foo")]
871struct Foo {
872 #[xml(text)]
873 a: String,
874};
875
876let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'>hello</foo>").unwrap();
877assert_eq!(foo, Foo {
878 a: "hello".to_string(),
879});
880```
881
882#### Example with codec
883
884```rust
885# use xso::FromXml;
886#[derive(FromXml, Debug, PartialEq)]
887#[xml(namespace = "urn:example", name = "foo")]
888struct Foo {
889 #[xml(text = xso::text::EmptyAsNone)]
890 a: Option<String>,
891};
892
893let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'/>").unwrap();
894assert_eq!(foo, Foo {
895 a: None,
896});
897```