from_xml_doc.md

  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```