Derive Macro xso::FromXml

source ·
#[derive(FromXml)]
{
    // Attributes available to this derive:
    #[xml]
}
Expand description

§Make a struct or enum parseable from XML

This macro generates the necessary trait implementations to convert a struct from XML.

In order to work, structs, enums, enum variants and fields all must be annotated using the #[xml(..)] meta. The insides of that meta are explained below.

§Examples

static MY_NAMESPACE: &'static str = "urn:uuid:55c56882-3915-49de-a7ee-fd672d7a85cf";

#[derive(FromXml)]
#[xml(namespace = MY_NAMESPACE, name = "foo")]
struct Foo;

// parses <foo xmlns="urn:uuid:55c56882-3915-49de-a7ee-fd672d7a85cf"/>

§Field order

Field order matters. The fields are parsed in the order they are declared (for children, anyway). If multiple fields match a given child element, the first field which matches will be taken. The only exception is #[xml(elements)] (without further arguments), which is always processed last.

When XML is generated from a struct, the child elements are also generated in the order of the fields. That means that passing an XML element through FromXml and IntoXml may re-order some child elements.

Sorting order between elements which match the same field is generally preserved, if the container preserves sort order on insertion.

§Struct attributes

  • namespace = ..: This can be one of the following:

    • dyn: allows dynamic namespace matching using the DynNamespaceEnum trait. A struct with this namespace type needs to have exactly one field with the #[xml(namespace)] annotation.

      The type of that field must implement DynNamespaceEnum and will be used to match the namespace of the XML elements.

    • A path referring to a &'static str static which contains the namespace URI of the XML element represented by the struct.

    Required on non-transparent structs.

  • name = ..: A string literal which contains the local XML name of of the XML element represented by the struct.

    Required on non-transparent structs.

  • validate = ..: A path referring to a fn(&mut T) -> Result<(), xso::error::Error>, where T is the struct which is being defined. If set, the function will be called after parsing has completed. If it returns an error, that error is returned instead of the struct.

    This attribute has no influence on IntoXml.

  • prepare = ..: A path referring to a fn(&mut T) -> (), where T is the struct which is being defined. If set, the function will be called before the struct is converted into Element.

    This attribute has no influence on FromXml.

  • transparent: Only allowed on tuple-like structs with exactly one field. If set, the parsing is fully delegated to the inner field, which must in turn implement FromXml (or IntoXml respectively).

    Attributes on the single struct field are rejected.

    validate is allowed and will be called.

  • element, element(..): Only allowed on tuple-like structs with exactly one field. That field must be of type [minidom::Element`].

    Supports the following optional inner attributes:

    • namespace: If given, restricts the namespace of the elements to parse. Has no influence on XML generation: If the inner element has a different namespace when the struct is serialized, that namespace will be used.

    • name: If given, restricts the XML name of the elements to parse. Has no influence on XML generation: If the inner element has a different XML name when the struct is serialized, that namespace will be used.

    validate is allowed and will be called.

  • on_unknown_child = .. may be set to the identifier of a member of the UnknownChildPolicy enum (i.e. for example on_unknown_child = Ignore). This configures the behavior when unknown child elements are encountered. See UnknownChildPolicy for details.

    Has no effect on grandchildren.

  • on_unknown_attribute = .. may be set to the identifier of a member of the UnknownAttributePolicy enum (i.e. for example on_unknown_attribute = Ignore). This configures the behavior when unknown attributes are encountered. See UnknownAttributePolicy for details.

    Has no effect on children.

  • wrapped_with(namespace = .., name = ..): If set, the struct will be wrapped into an XML element with the given namespace and name. That means that instead of <inner/>, on the wire, <outer><inner/></outer> (with the corresponding XML names and namespaces) is expected and generated.

    Other than the struct itself, the wrapping element must not have any attributes or child elements, and it only supports static namespaces.

§Enums

Enums come in multiple flavors. All flavors have the following attributes:

  • validate = ..: See struct attributes.
  • prepare = ..: See struct attributes.
  • wrapped_with = ..: See struct attributes.

The following flavors exist:

  • fully dynamic: The variants must be each either transparent or specify their namespace and name.

  • XML name matched: The enum itself defines a namespace. Each variant must then specify the name. The variant is picked based on the name of the XML element.

  • XML attribute matched: The enum itself defines a namespace, a name and an attribute name. Each variant must specify the attribute value. The variant is picked based on the value of the given attribute, provided that XML name and namespace of the element itself match.

The flavor is determined based on the attributes of the enum declaration.

§Dynamic enums

No additional attributes are available on dynamic enumerations.

§Dynamic enum variants

Dynamic enum variants work exactly like structs, except that the prepare and validate attributes are not available.

§XML name matched enums

XML name matched enums support the following attributes:

  • namespace = .. (required): This must be a path to a &'static str. It is the namespace of the enumeration.

  • exhaustive (flag): If present, the enum considers itself authoritative for that namespace. If it encounters an element within the namespace which does not match any variant, a fatal parsing error is returned.

    This cannot be used if a variant is set as fallback.

    This attribute has no relation to the Rust standard #[non_exhaustive] attribute.

§XML name matched enum variants

XML name matched enum variants support the following attributes:

  • name = .. (required): String literal with the XML name to match against.

  • fallback (flag): If present, the variant is parsed when no other variant matches.

    Note: When the enum is reserialized to XML, the XML name will be the one declared in the name attribute of the variant; the original XML name is lost.

§XML attribute matched enums

XML attribute matched enums support the following attributes:

  • namespace = .. (required): This must be a path to a &'static str. It is the namespace of the enumeration.

  • name = .. (required): This must be a string literal containing the XML name to match against.

  • attribute = .. (required): This must be a string literal with the name of the XML attribute to match against.

  • exhaustive (flag): Must currently be set unless a variant is marked as fallback. Support for non-exhaustive attribute-matched enums is not implemented yet.

    This cannot be used if a variant is set as fallback.

    This attribute has no relation to the Rust standard #[non_exhaustive] attribute.

  • normalize_with = ..: Optional path to a thing which can be called with a &str and which returns a std::borrow::Cow. If present, the attribute value will be passed through that callable before it will be matched against the enum variants.

§XML attribute matched enum variants

XML attribute matched enum variants support the following attributes:

  • value = .. (required): String literal with the attribute value to match against.

  • fallback (flag): If present, the variant is parsed when no other variant matches.

    Note: When the enum is reserialized to XML, the attribute value will be the one declared in the value attribute of the variant; the original value is lost.

§Field attributes

Field attributes are composed of a field kind, followed by a value or a list of attributes. Examples:

#[xml(attribute)]
#[xml(attribute = "foo")]
#[xml(attribute(name = "foo"))]

If the kind = .. syntax is allowed, the attribute which is specified that way will be marked as default.

The following field kinds are available:

  • attribute, attribute = name, attribute(..): Extract a string from an XML attribute. The field type must implement FromOptionalXmlText (for FromXml) or IntoOptionalXmlText (for IntoXml), unless the codec option is set.

    • name = .. (default): The XML name of the attribute. If this is not set, the field’s identifier is used.

    • namespace = ..: The XML namespace of the attribute. This is optional, and if absent, only unnamespaced attributes are considered.

    • default, default = ..: If set, a field value is generated if the attribute is not present and FromOptionalXmlText did not create a value from None, instead of failing to parse. If the optional argument is present, it must be the path to a callable which returns the field’s type. Otherwise, std::default::Default::default is used.

    • codec = ..: Path to a type implementing TextCodec to use instead of the FromOptionalXmlText / IntoOptionalXmlText implementation of the field’s type.

      If set, you need to explicitly add the default flag to fields of type Option<_>, because the default option logic of FromOptionalXmlText is not present.

  • child(.., extract(..)): Extract data from a child element.

    • name = .. (required): The XML name of the child to match.

    • namespace = .. (required): The XML namespace of the child to match. This can be one of the following:

      • A path referring to a &'static str static which contains the namespace URI of the XML element represented by the struct.
      • super: Only usable inside compounds with #[xml(namespace = dyn)], using #[xml(namespace = super)] on an extracted field allows to match the field’s child’s namespace with the dynamically determined namespace of the parent (both during serialisation and during deserialisation).
    • extract(..) (required): Specification of data to extract. See below for options.

    • skip_if: If set, this must be the path to a callable. That callable is invoked with a reference to the field’s type at serialisation time. If the callable returns true, the field is omitted from the output completely.

      This should often be combined with default.

    • default, default = ..: If set, a field value is generated if the child is not present instead of failing to parse. If the optional argument is present, it must be the path to a callable which returns the field’s type. Otherwise, std::default::Default::default is used.

      Note: When using extract(..), this is required even when the field’s type is Option<..>.

  • child, child(..) (without extract(..)): Extract an entire child element. The field type must implement FromXml (for FromXml) or IntoXml (for IntoXml).

    • namespace = super: If set, the field must also implement DynNamespace and the compound the field is in must be set to be namespace = dyn. In this case, the field’s child is forced to be in the same namespace as the parent during parsing.

    • skip_if: If set, this must be the path to a callable. That callable is invoked with a reference to the field’s type at serialisation time. If the callable returns true, the field is omitted from the output completely.

      This should often be combined with default.

    • default, default = ..: If set, a field value is generated if the child is not present instead of failing to parse. If the optional argument is present, it must be the path to a callable which returns the field’s type. Otherwise, std::default::Default::default is used.

      Note: When using extract(..), this is required even when the field’s type is Option<..>.

    Aside from namespace = super, matching of the XML namespace / name is completely delegated to the FromXml implementation of the field’s type and thus the namespace and name attributes are not allowed.

  • children(.., extract(..)): Like child(.., extract(..)), with the following differences:

    • More than one clause inside extract(..) are allowed.

    • More than one matching child is allowed

    • The field type must implement Default, Extend<T> and IntoIterator<Item = T>.

      T, must be a tuple type matching the types provided by the extracted parts.

    • Extracts must specify their type, because it cannot be inferred through the collection.

  • children, children(..) (without extract(..)): Extract zero or more entire child elements. The field type must implement Default, Extend<T> and IntoIterator<Item = T>, where T implements FromXml (and IntoXml for IntoXml).

    • skip_if: If set, this must be the path to a callable. That callable is invoked with a reference to the field’s type at serialisation time. If the callable returns true, the field is omitted from the output completely.

      This should often be combined with default.

    The namespace and name to match are determined by the field type, thus it is not allowed to specify them here. namespace = super is not supported.

  • text, text(..): Extract the element's text contents. The field type must implement FromXmlText (for [FromXml]) or IntoXmlText (for [IntoXml]), unless the codec` option is set.

  • element, element(..): Collect a single element as minidom::Element instead of attempting to destructure it. The field type must implement From<Element> and Into<Option<Element>> (minidom::Element implements both).

    • name = .. (optional): The XML name of the element to match.
    • namespace = .. (optional): The XML namespace of the element to match.
    • default, default = ..: If set, a field value is generated if the child is not present instead of failing to parse. If the optional argument is present, it must be the path to a callable which returns the field’s type. Otherwise, std::default::Default::default is used.

    If the field converts into None when invoking Into<Option<Element>>, the element is omitted from the output altogether.

  • elements(..): Collect otherwise unknown children as minidom::Element.

    • namespace = ..: The XML namespace of the element to match.
    • name = .. (optional): The XML name of the element to match. If omitted, all elements from the given namespace are collected.
  • elements: Collect all unknown children as minidom::Element. The field type must be Vec<Element>.

  • namespace: Represent the parent struct/enum variant’s XML namespace. This requires that the compound is declared with #[xml(namespace = dyn)]. The field type must implement DynNamespaceEnum.

  • ignore: The field is not considered during parsing or serialisation. The type must implement Default.

§Extraction specification

Inside extract(..), there must be a list of type annotations as used on fields. All annotations can be used which can also be used on fields. Because here there is no possibility to do any inferrence on the field type, the following field attributes support an additional, optional type argument:

  • attribute
  • text

If the extract(..) contains exactly one part and the type of the extract is not specified on that one part, it is assumed to be equal to the type of the field the extract is used on.

Otherwise, the default is String, which is not going to work in many cases. This limitation could be lifted, but we need a use case for it first :). So if you run into this file an issue please! Derive macro for FromXml.