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