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. [Dynamic enum meta](#dynamic-enum-meta)
254. [Field meta](#field-meta)
26 1. [`attribute` meta](#attribute-meta)
27 2. [`child` meta](#child-meta)
28 3. [`element` meta](#element-meta)
29 4. [`extract` meta](#extract-meta)
30 5. [`text` meta](#text-meta)
31
32## Attributes
33
34The derive macros need additional information, such as XML namespaces and
35names to match. This must be specified via key-value pairs on the type or
36fields the derive macro is invoked on. These key-value pairs are specified as
37Rust attributes. In order to disambiguate between XML attributes and Rust
38attributes, we are going to refer to Rust attributes using the term *meta*
39instead, which is consistent with the Rust language reference calling that
40syntax construct *meta*.
41
42All key-value pairs interpreted by these derive macros must be wrapped in a
43`#[xml( ... )]` *meta*.
44
45The values associated with the keys may be of different types, defined as
46such:
47
48- *path*: A Rust path, like `some_crate::foo::Bar`. Note that `foo` on its own
49 is also a path.
50- *string literal*: A string literal, like `"hello world!"`.
51- *type*: A Rust type.
52- *expression*: A Rust expression.
53- *ident*: A Rust identifier.
54- *nested*: The meta is followed by parentheses, inside of which meta-specific
55 additional keys are present.
56- flag: Has no value. The key's mere presence has relevance and it must not be
57 followed by a `=` sign.
58
59## Struct meta
60
61The following keys are defined on structs:
62
63| Key | Value type | Description |
64| --- | --- | --- |
65| `namespace` | *string literal* or *path* | The XML element namespace to match. If it is a *path*, it must point at a `&'static str`. |
66| `name` | *string literal* or *path* | The XML element name to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
67| `transparent` | *flag* | If present, declares the struct as *transparent* struct (see below) |
68| `builder` | optional *ident* | The name to use for the generated builder type. |
69| `iterator` | optional *ident* | The name to use for the generated iterator type. |
70
71Note that the `name` value must be a valid XML element name, without colons.
72The namespace prefix, if any, is assigned automatically at serialisation time
73and cannot be overridden. The following will thus not compile:
74
75```compile_fail
76# use xso::FromXml;
77#[derive(FromXml, Debug, PartialEq)]
78#[xml(namespace = "urn:example", name = "fnord:foo")] // colon not allowed
79struct Foo;
80```
81
82If `builder` or `iterator` are given, the respective generated types will use
83the given names instead of names chosen by the derive macro implementation.
84Helper types will use these names as prefix. The exact names of helper types
85are implementation defined, which is why any type name starting with the
86identifiers passed to either of these keys is considered reserved.
87
88By default, the builder type uses the type's name suffixed with
89`FromXmlBuilder` and the iterator type uses the type's name suffixed with
90`AsXmlIterator`.
91
92If the struct is marked as `transparent`, it must not have a `namespace` or
93`name` set and it must have exactly one field. That field's type must
94implement [`FromXml`] in order to derive `FromXml` and [`AsXml`] in order to
95derive `AsXml`. The struct will be (de-)serialised exactly like the type of
96that single field. This allows a newtype-like pattern for XSO structs.
97
98## Enums
99
100Two different `enum` flavors are supported:
101
1021. [**Name-switched enums**](#name-switched-enum-meta) have a fixed XML
103 namespace they match on and each variant corresponds to a different XML
104 element name within that namespace.
105
1062. [**Dynamic enums**](#dynamic-enum-meta) have entirely unrelated variants.
107
108At the source-code level, they are distinguished by the meta keys which are
109present on the `enum`: The different variants have different sets of mandatory
110keys and can thus be uniquely identified.
111
112### Name-switched enum meta
113
114Name-switched enums match a fixed XML namespace and then select the enum
115variant based on the XML element's name. Name-switched enums are declared by
116setting the `namespace` key on a `enum` item.
117
118The following keys are defined on name-switched enums:
119
120| Key | Value type | Description |
121| --- | --- | --- |
122| `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`. |
123| `builder` | optional *ident* | The name to use for the generated builder type. |
124| `iterator` | optional *ident* | The name to use for the generated iterator type. |
125| `exhaustive` | *flag* | If present, the enum considers itself authoritative for its namespace; unknown elements within the namespace are rejected instead of treated as mismatch. |
126
127All variants of a name-switched enum live within the same namespace and are
128distinguished exclusively by their XML name within that namespace. The
129contents of the XML element (including attributes) is not inspected before
130selecting the variant when parsing XML.
131
132If *exhaustive* is set and an element is encountered which matches the
133namespace of the enum, but matches none of its variants, parsing will fail
134with an error. If *exhaustive* is *not* set, in such a situation, parsing
135would attempt to continue with other siblings of the enum, attempting to find
136a handler for that element.
137
138Note that the *exhaustive* flag is orthogonal to the Rust attribute
139`#[non_exhaustive]`.
140
141For details on `builder` and `iterator`, see the [Struct meta](#struct-meta)
142documentation above.
143
144#### Name-switched enum variant meta
145
146| Key | Value type | Description |
147| --- | --- | --- |
148| `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`. |
149
150Note that the `name` value must be a valid XML element name, without colons.
151The namespace prefix, if any, is assigned automatically at serialisation time
152and cannot be overridden.
153
154#### Example
155
156```rust
157# use xso::FromXml;
158#[derive(FromXml, Debug, PartialEq)]
159#[xml(namespace = "urn:example")]
160enum Foo {
161 #[xml(name = "a")]
162 Variant1 {
163 #[xml(attribute)]
164 foo: String,
165 },
166 #[xml(name = "b")]
167 Variant2 {
168 #[xml(attribute)]
169 bar: String,
170 },
171}
172
173let foo: Foo = xso::from_bytes(b"<a xmlns='urn:example' foo='hello'/>").unwrap();
174assert_eq!(foo, Foo::Variant1 { foo: "hello".to_string() });
175
176let foo: Foo = xso::from_bytes(b"<b xmlns='urn:example' bar='hello'/>").unwrap();
177assert_eq!(foo, Foo::Variant2 { bar: "hello".to_string() });
178```
179
180### Dynamic enum meta
181
182Dynamic enums select their variants by attempting to match them in declaration
183order. Dynamic enums are declared by not setting the `namespace` key on an
184`enum` item.
185
186The following keys are defined on dynamic enums:
187
188| Key | Value type | Description |
189| --- | --- | --- |
190| `builder` | optional *ident* | The name to use for the generated builder type. |
191| `iterator` | optional *ident* | The name to use for the generated iterator type. |
192
193For details on `builder` and `iterator`, see the [Struct meta](#struct-meta)
194documentation above.
195
196#### Dynamic enum variant meta
197
198Dynamic enum variants are completely independent of one another and thus use
199the same meta structure as structs. See [Struct meta](#struct-meta) for
200details.
201
202The `builder`, `iterator` and `debug` keys cannot be used on dynmaic enum
203variants.
204
205#### Example
206
207```rust
208# use xso::FromXml;
209#[derive(FromXml, Debug, PartialEq)]
210#[xml()]
211enum Foo {
212 #[xml(namespace = "urn:example:ns1", name = "a")]
213 Variant1 {
214 #[xml(attribute)]
215 foo: String,
216 },
217 #[xml(namespace = "urn:example:ns2", name = "b")]
218 Variant2 {
219 #[xml(attribute)]
220 bar: String,
221 },
222}
223
224let foo: Foo = xso::from_bytes(b"<a xmlns='urn:example:ns1' foo='hello'/>").unwrap();
225assert_eq!(foo, Foo::Variant1 { foo: "hello".to_string() });
226
227let foo: Foo = xso::from_bytes(b"<b xmlns='urn:example:ns2' bar='hello'/>").unwrap();
228assert_eq!(foo, Foo::Variant2 { bar: "hello".to_string() });
229```
230
231## Field meta
232
233For fields, the *meta* consists of a nested meta inside the `#[xml(..)]` meta,
234the identifier of which controls *how* the field is mapped to XML, while the
235contents control the parameters of that mapping.
236
237The following mapping types are defined:
238
239| Type | Description |
240| --- | --- |
241| [`attribute`](#attribute-meta) | Map the field to an XML attribute on the struct's element |
242| [`child`](#child-meta) | Map the field to a child element |
243| [`element`](#element-meta) | Map the field to a child element as [`minidom::Element`] |
244| [`extract`](#extract-meta) | Map the field to contents of a child element of specified structure |
245| [`text`](#text-meta) | Map the field to the text content of the struct's element |
246
247### `attribute` meta
248
249The `attribute` meta causes the field to be mapped to an XML attribute of the
250same name. For `FromXml`, the field's type must implement [`FromXmlText`] and
251for `AsXml`, the field's type must implement [`AsOptionalXmlText`].
252
253The following keys can be used inside the `#[xml(attribute(..))]` meta:
254
255| Key | Value type | Description |
256| --- | --- | --- |
257| `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. |
258| `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`. |
259| `default` | flag | If present, an absent attribute will substitute the default value instead of raising an error. |
260| `type_` | *type* | Optional explicit type specification. Only allowed within `#[xml(extract(fields(..)))]`. |
261
262If the `name` key contains a namespace prefix, it must be one of the prefixes
263defined as built-in in the XML specifications. That prefix will then be
264expanded to the corresponding namespace URI and the value for the `namespace`
265key is implied. Mixing a prefixed name with an explicit `namespace` key is
266not allowed.
267
268The `attribute` meta also supports a shorthand syntax,
269`#[xml(attribute = ..)]`, where the value is treated as the value for the
270`name` key (with optional prefix as described above, and unnamespaced
271otherwise).
272
273If `default` is specified and the attribute is absent in the source, the value
274is generated using [`core::default::Default`], requiring the field type to
275implement the `Default` trait for a `FromXml` derivation. `default` has no
276influence on `AsXml`.
277
278If `type_` is specified and the `text` meta is used within an
279`#[xml(extract(fields(..)))]` meta, the specified type is used instead of the
280field type on which the `extract` is declared.
281
282#### Example
283
284```rust
285# use xso::FromXml;
286#[derive(FromXml, Debug, PartialEq)]
287#[xml(namespace = "urn:example", name = "foo")]
288struct Foo {
289 #[xml(attribute)]
290 a: String,
291 #[xml(attribute = "bar")]
292 b: String,
293 #[xml(attribute(name = "baz"))]
294 c: String,
295 #[xml(attribute(namespace = "urn:example", name = "fnord"))]
296 d: String,
297 #[xml(attribute = "xml:lang")]
298 e: String,
299};
300
301let foo: Foo = xso::from_bytes(b"<foo
302 xmlns='urn:example'
303 a='1' bar='2' baz='3'
304 xmlns:tns0='urn:example' tns0:fnord='4'
305 xml:lang='5'
306/>").unwrap();
307assert_eq!(foo, Foo {
308 a: "1".to_string(),
309 b: "2".to_string(),
310 c: "3".to_string(),
311 d: "4".to_string(),
312 e: "5".to_string(),
313});
314```
315
316### `child` meta
317
318The `child` meta causes the field to be mapped to a child element of the
319element.
320
321The following keys can be used inside the `#[xml(child(..))]` meta:
322
323| Key | Value type | Description |
324| --- | --- | --- |
325| `default` | flag | If present, an absent child will substitute the default value instead of raising an error. |
326| `n` | `1` or `..` | If `1`, a single element is parsed. If `..`, a collection is parsed. Defaults to `1`. |
327
328When parsing a single child element (i.e. `n = 1` or no `n` value set at all),
329the field's type must implement [`FromXml`] in order to derive `FromXml` and
330[`AsXml`] in order to derive `AsXml`.
331
332When parsing a collection (with `n = ..`), the field's type must implement
333[`IntoIterator<Item = T>`][`core::iter::IntoIterator`], where `T` must
334implement [`FromXml`] in order to derive `FromXml` and [`AsXml`] in order to
335derive `AsXml`. In addition, the field's type must implement
336[`Extend<T>`][`core::iter::Extend`] to derive `FromXml` and the field's
337reference type must implement `IntoIterator<Item = &'_ T>` to derive `AsXml`.
338
339If `default` is specified and the child is absent in the source, the value
340is generated using [`core::default::Default`], requiring the field type to
341implement the `Default` trait for a `FromXml` derivation. `default` has no
342influence on `AsXml`. Combining `default` and `n` where `n` is not set to `1`
343is not supported and will cause a compile-time error.
344
345Using `default` with a type other than `Option<T>` will cause the
346serialisation to mismatch the deserialisation (i.e. the struct is then not
347roundtrip-safe), because the deserialisation does not compare the value
348against `default` (but has special provisions to work with `Option<T>`).
349
350#### Example
351
352```rust
353# use xso::FromXml;
354#[derive(FromXml, Debug, PartialEq)]
355#[xml(namespace = "urn:example", name = "child")]
356struct Child {
357 #[xml(attribute = "some-attr")]
358 some_attr: String,
359}
360
361#[derive(FromXml, Debug, PartialEq)]
362#[xml(namespace = "urn:example", name = "other-child")]
363struct OtherChild {
364 #[xml(attribute = "some-attr")]
365 some_attr: String,
366}
367
368#[derive(FromXml, Debug, PartialEq)]
369#[xml(namespace = "urn:example", name = "parent")]
370struct Parent {
371 #[xml(attribute)]
372 foo: String,
373
374 #[xml(child)]
375 bar: Child,
376
377 #[xml(child(n = ..))]
378 baz: Vec<OtherChild>,
379}
380
381let parent: Parent = xso::from_bytes(b"<parent
382 xmlns='urn:example'
383 foo='hello world!'
384><child
385 some-attr='within'
386/><other-child
387 some-attr='c1'
388/><other-child
389 some-attr='c2'
390/></parent>").unwrap();
391assert_eq!(parent, Parent {
392 foo: "hello world!".to_owned(),
393 bar: Child { some_attr: "within".to_owned() },
394 baz: vec! [
395 OtherChild { some_attr: "c1".to_owned() },
396 OtherChild { some_attr: "c2".to_owned() },
397 ],
398});
399```
400
401### `element` meta
402
403The `element` meta causes the field to be mapped to child elements, stored as
404a container containing [`minidom::Element`] instances.
405
406This meta is only available if `xso` is being built with the `"minidom"`
407feature.
408
409The following keys can be used inside the `#[xml(extract(..))]` meta:
410
411| Key | Value type | Description |
412| --- | --- | --- |
413| `n` | `..` | Must be set to the value `..`. |
414
415The `n` parameter will, in the future, support values other than `..`. In
416order to provide a non-breaking path into that future, it must be set to the
417value `..` right now, indicating that an arbitrary number of elements may be
418collected by this meta.
419
420The field's type must be a collection of `minidom::Element`. It must thus
421implement
422[`IntoIterator<Item = minidom::Element>`][`core::iter::IntoIterator`]. In
423addition, the field's type must implement
424[`Extend<minidom::Element>`][`core::iter::Extend`] to derive `FromXml` and the
425field's reference type must implement
426`IntoIterator<Item = &'_ minidom::Element>` to derive `AsXml`.
427
428Fields with the `element` meta are deserialised with the lowest priority.
429While other fields are processed in the order they are declared, `element`
430fields may capture arbitrary child elements, so they are considered as the
431last choice when no other field matched a given child element. In addition,
432it is not allowed to have more than one field in any given struct with the
433`#[xml(element)]` meta.
434
435#### Example
436
437```rust
438# #[cfg(feature = "minidom")]
439# {
440# use xso::FromXml;
441# use xso::exports::minidom;
442#[derive(FromXml, Debug, PartialEq)]
443#[xml(namespace = "urn:example", name = "parent")]
444struct Parent {
445 #[xml(element(n = ..))]
446 misc: Vec<minidom::Element>,
447}
448
449let parent: Parent = xso::from_bytes(b"<parent
450 xmlns='urn:example'
451><child-a/><child-b/><child-a/></parent>").unwrap();
452assert_eq!(parent.misc[0].name(), "child-a");
453assert_eq!(parent.misc[1].name(), "child-b");
454assert_eq!(parent.misc[2].name(), "child-a");
455# }
456```
457
458### `extract` meta
459
460The `extract` meta causes the field to be mapped to the *contents* of a child
461element.
462
463The following keys can be used inside the `#[xml(extract(..))]` meta:
464
465| Key | Value type | Description |
466| --- | --- | --- |
467| `namespace` | *string literal* or *path* | The XML namespace of the child element. |
468| `name` | *string literal* or *path* | The XML name of the child element. If it is a *path*, it must point at a `&'static NcNameStr`. |
469| `default` | flag | If present, an absent child will substitute the default value instead of raising an error. |
470| `n` | `1` or `..` | If `1`, a single element is parsed. If `..`, a collection is parsed. Defaults to `1`. |
471| `fields` | *nested* | A list of [field meta](#field-meta) which describe the contents of the child element. |
472
473If the `name` key contains a namespace prefix, it must be one of the prefixes
474defined as built-in in the XML specifications. That prefix will then be
475expanded to the corresponding namespace URI and the value for the `namespace`
476key is implied. Mixing a prefixed name with an explicit `namespace` key is
477not allowed.
478
479Both `namespace` and `name` may be omitted. If `namespace` is omitted, it
480defaults to the namespace of the surrounding container. If `name` is omitted
481and the `extract` meta is being used on a named field, that field's name is
482used. If `name` is omitted and `extract` is not used on a named field, an
483error is emitted.
484
485When parsing a single child element (i.e. `n = 1` or no `n` value set at all),
486the extracted field's type is set to be the same type as the field on which
487the extract is declared, unless overridden in the extracted field's meta.
488
489When parsing a collection (with `n = ..`), the extracted fields within
490`fields()` must all have type specifications. Not all fields kinds support
491that.
492
493The sequence of field meta inside `fields` can be thought of as a nameless
494tuple-style struct. The macro generates serialisation/deserialisation code
495for that nameless tuple-style struct and uses it to serialise/deserialise
496the field.
497
498If `default` is specified and the child is absent in the source, the value
499is generated using [`core::default::Default`], requiring the field type to
500implement the `Default` trait for a `FromXml` derivation. `default` has no
501influence on `AsXml`. Combining `default` and `n` where `n` is not set to `1`
502is not supported and will cause a compile-time error.
503
504Mixing `default` on the `#[xml(extract)]` itself with `default` on the
505extracted inner fields creates non-roundtrip-safe parsing, unless you also
506use twice-nested [`core::option::Option`] types. That means that when
507deserialising a piece of XML and reserialising it without changing the
508contents of the struct in Rust, the resulting XML may not match the input.
509This is because to the serialiser, if only one layer of
510[`core::option::Option`] is used, it is not possible to distinguish which of
511the two layers were defaulted. The exact behaviour on serialisation in such a
512situation is *not guaranteed* and may change between versions of the `xso`
513crate, its dependencies, the standard library, or even rustc itself.
514
515Using `default` with a type other than `Option<T>` will cause the
516serialisation to mismatch the deserialisation, too (i.e. the struct is then
517not roundtrip-safe), because the deserialisation does not compare the value
518against `default` (but has special provisions to work with `Option<T>`).
519
520If more than one single field is contained in `fields`, the fields will be
521extracted as a tuple in the order they are given in the meta. In addition, it
522is required to explicitly specify each extracted field's type in that case.
523
524Using `extract` instead of `child` combined with a specific struct declaration
525comes with trade-offs. On the one hand, using `extract` gives you flexibility
526in regard of the specific serialisation of a field: it is possible to exchange
527a nested child element for an attribute without changing the Rust interface
528of the struct.
529
530On the other hand, `extract` meta declarations can quickly become unwieldly
531and they may not support all configuration options which may in the future be
532added on structs (such as configuring handling of undeclared attributes) and
533they cannot be used for enumerations.
534
535#### Example
536
537```rust
538# use xso::FromXml;
539#[derive(FromXml, Debug, PartialEq)]
540#[xml(namespace = "urn:example", name = "foo")]
541struct Foo {
542 #[xml(extract(namespace = "urn:example", name = "bar", fields(attribute = "a")))]
543 a: String,
544}
545
546let foo: Foo = xso::from_bytes(b"<foo
547 xmlns='urn:example'><bar a='xyz'/></foo>").unwrap();
548assert_eq!(foo, Foo {
549 a: "xyz".to_string(),
550});
551```
552
553### `text` meta
554
555The `text` meta causes the field to be mapped to the text content of the
556element.
557
558| Key | Value type | Description |
559| --- | --- | --- |
560| `codec` | *expression* | Optional [`TextCodec`] implementation which is used to encode or decode the field. |
561| `type_` | *type* | Optional explicit type specification. Only allowed within `#[xml(extract(fields(..)))]`. |
562
563If `codec` is given, the given `codec` value must implement
564[`TextCodec<T>`][`TextCodec`] where `T` is the type of the field.
565
566If `codec` is *not* given, the field's type must implement [`FromXmlText`] for
567`FromXml` and for `AsXml`, the field's type must implement [`AsXmlText`].
568
569If `type_` is specified and the `text` meta is used within an
570`#[xml(extract(fields(..)))]` meta, the specified type is used instead of the
571field type on which the `extract` is declared.
572
573The `text` meta also supports a shorthand syntax, `#[xml(text = ..)]`, where
574the value is treated as the value for the `codec` key (with optional prefix as
575described above, and unnamespaced otherwise).
576
577Only a single field per struct may be annotated with `#[xml(text)]` at a time,
578to avoid parsing ambiguities. This is also true if only `AsXml` is derived on
579a field, for consistency.
580
581#### Example without codec
582
583```rust
584# use xso::FromXml;
585#[derive(FromXml, Debug, PartialEq)]
586#[xml(namespace = "urn:example", name = "foo")]
587struct Foo {
588 #[xml(text)]
589 a: String,
590};
591
592let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'>hello</foo>").unwrap();
593assert_eq!(foo, Foo {
594 a: "hello".to_string(),
595});
596```
597
598#### Example with codec
599
600```rust
601# use xso::FromXml;
602#[derive(FromXml, Debug, PartialEq)]
603#[xml(namespace = "urn:example", name = "foo")]
604struct Foo {
605 #[xml(text = xso::text::EmptyAsNone)]
606 a: Option<String>,
607};
608
609let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example'/>").unwrap();
610assert_eq!(foo, Foo {
611 a: None,
612});
613```