### ----------------------------------------------------
### -- LibXML::Class
### -- Licenses: Artistic-2.0
### -- Authors: Vadim Belman
### -- File: docs/md/LibXML/Class/Manual.md
### ----------------------------------------------------
### -- Chunk 2 of 2
-map => ( Record, "other-ns" => {...} );
```

This is, roughly, equivalent to:

```raku
my LibXML::Class::Config $config .= new: ns-map => { "my-ns" => {'rec' => Record}, "other-ns" => {...} };
```

Sometimes we don't care about what namespace a type object declares and all we want is its XML name. Then a namespace can be declared with a list as its value:

```raku
my LibXML::Class::Config $config .= new: ns-map => ( "other-ns" => (Record, "foo" => Foo) );
```

Some of possible variants are demoed in [*manual09.raku*](../../../../examples/manual09.raku).

XML Sequence Objects
--------------------

At the first glance, XML sequence (later in this section the term would often be shortened to just *sequence*) in the [Some Terminology](Some Terminology) section of this manual looks a lot like a containerized [`Array`](https://docs.raku.org/type/Array) with items of different types. Though it isn't. The key differences are:

  * A sequence natively supports items of different types. One can do it for arrays too with help of [`subset`](https://docs.raku.org/language/typesystem#subset), but it would be a hassle. As it would be shown below, declaring class a sequence is more readable.

  * Sequences are always lazy. Just always, no exceptions, no respect to [`LibXML::Class::Config`](Config.md) `eager` flag.

    One could point out that an array attribute could be marked lazy too. There is a catch though: the array is anyway deserialized as a whole, all items at once. A sequence is lazy on per-item basis. I.e. when there is something like `@.list[42]` the entire `@.list` will be vivified, even if it consists of hunderds of items. Contrary, for a sequence `$.list[42]` would mean that the sequence itself would be vivified first (unless it's not done yet), then the item at position 42 is deserialized and returned for our use. Referencing, say, `$.list[12]` later would only result in deserialization of the 12th position as the sequence object is already there.

    This makes sequences great when dealing with *long* lists of values.

  * What an array is most definitely not capable of is mainaining individual de-/serialization properties on per-type basis. For example, if our sequence type object is configured for items of `Foo` and `Bar` type then for each one we can individually configure namespace parameters, serializer, and deserializer.

  * XML sequences can contain non-item elements too. From the Raku language point of view it means they can have serializable attributes:

    ```xml
    <my-seq>
      <rec1>val1</rec1>
      <rec2 value="foo1" />
      <rec2 value="foo2" />
      <foo value="this comes from $.foo attribute"/>
      <counter>42</counter> <!-- has Int $.counter; -->
    </my-seq>
    ```

  * A sequence type can be a composition of other sequence and non-sequence types. For a type to start behaving as a sequential it is enough for just any of its roles or parents to be one.

### Declaring An XML Sequence

Sequences are declared with help of `:sequence` named argument of `xml-element` type object declaration:

```raku
class References is xml-element( :sequence( :idx(Int:D), :ref(Str:D, :attr<title>) ) ) {
    has Str:D $.title is required;
}
```

Here we define a sequence which can consist of integer or string items. Following is an example of using the sequence from [*manual07.raku*](../../../../examples/manual07.raku):

```raku
my $refs = References.new: :title('An Article');
$refs.push: 123456;
$refs.push: "3rd Party Article";
$refs.push: "Another Article";
$refs.push: 987654;
```

It serializes into:

```xml
<References title="An Article">
  <idx>123456</idx>
  <ref title="3rd Party Article"/>
  <ref title="Another Article"/>
  <idx>987654</idx>
</References>
```

The example in [*manual08.raku*](../../../../examples/manual08.raku) demonstrates that using an `xml-element` class as a sequence item type is even easier as it would normally has all we need to deserialize it:

```raku
class Ref is xml-element<ref> {...}
class Index is xml-element('index', :sequence( Ref, :idx(Int:D) )) {...}
```

### Item Parameters

In a way, a sequence item shares some properties with attributes. After all, attributes can be serialized into elements, same as items do. Internally both are handled with help of *descriptors* that inherit from the same descriptor parent class. That makes it possible for items to have certain parameters adjusted using named arguments:

```raku
class Index
    is xml-element(
        :sequence(
            ( Ref, :derive ),
            :idx( Int:D, :ns('some-ns') ),
            :meta( Str:D, :attr<data> ) ))
{...}
```

The following arguments are supported:

  * `:attr(Str:D)` or `:value-attr(Str:D)`

    These are aliases of the same thing: name of XML attribute to contain basic type value.

  * `:namespace(...)`, `:ns(...)`

    Again, just two aliases. Define item namespace profile.

  * `:derive`

    Turns on or off attribute namespace deriving mode. See the section [Namespaces](#Namespaces).

  * `:&serializer`, `:&deserializer`

    User provided serialization/deserialization.

### XML:any Items

Normally it doesn't make sense to use `:any` argument when declaring an `xml-element` type object. Unless for XML sequences. Marking one as XML:any allows to use `ns-map` for sequence items.

There are two screnarios where XML:any helps with item de-/serialization ([*manual10.raku*](../../../../examples/manual10.raku)).

  * Basic type

    Since basic types doesn't have names binding them to an XML element name could be done with the help of `ns-map`.

  * Role-constrained items

    Even though it is possible to declare an item as `:elem(SomeRole)` the declaration barely makes much sense for deserialization because there is no way to know what class to deserialize an `<elem ...>` into. As a best guess we can pun the `SomeRole` role, but the resulting class is sufficiently likely to be useless. But XML:any could be very handy here.

XML:any item elements are not getting wrapped into any kind of container, except for the sequence tag itself which is a container on its own, as a matter of fact.

Otherwise they way XML:any works for items is no different what how it does for attributes.

### Is Item Deserialized?

Since the sequences are always lazy it may be needed to know if an item has been deserialized alredy or not. In a way, this could be done using the `EXISTS-POS` method on a sequence, or `:exists` adverb on `[]`-operator. But there are considerations which make this approach not very reliable.

First, an item could be deserialized but later removed with the `DELETE-POS` method or the `:delete` adverb. Second, a new item could be pushed onto the sequence which hasn't came from deserialization.

Therefore, in addition to the methods, standard for [`Array`](https://docs.raku.org/type/Array), XML sequences introduce an additional one: `HAS-POS`. The method is also backed by the `:has` adverb on `[]`:

```raku
$xml-sequence.HAS-POS(1);
$xml-sequence[1]:has;
$xml-sequence[1]:!has;
```

The method would report an item as deserialized even if it's been later deleted and `EXISTS-POS` is reporting *False*.

Namespaces
----------

`LibXML::Class` tries to follow XML rules whenever possible when it comes to namespaces. However, Raku is not XML, apparently, and what is natural to the latter may not be as natural for the former. To make it all easier the module tries to mainain reasonable defaults while allowing to do fine-tuning when necessary.

Here is the basic rules of namespacing in `LibXML::Class`:

  * An object namespace information may have a prefix (the *"pfx"* in `<pfx:foo/>`), a default namespace (*"default"* in `<foo xml-ns="default" /`), and prefix definition registry where prefixes are mapped into respective namespaces.

  * For every object namespace is defined by its prefix if set; otherwise the default namespace is used.

  * Raku attribute declaration is the primary authority for any namespace information. If the attruibute declaration doesn't provide any then class declaration is used if attribute's type is an `xml-element` type object.

  * If an object doesn't get its namespace neither for the attribute nor from its class then the default namespace of attribute's declarator is used.

These basics can be seen in action in [*manual11.raku*](../../../../examples/manual11.raku). Pay attention to how overriding the default namespace with `xml-default-ns` profile key affects the result where elements with no explicit namespaces still are bound to the default, whereas `$.rec` sticks to what is declared for `Record`.

It is expected that this behavior would cover most cases of de-/serializing with namespaces.

### Declaring Namespaces With `:namespace`/`:ns` Argument

The common argument to declare namespace information is `:namespace`, or its shorter alias `:ns`. The arguments itself can take arguments but can also be used as a flag with attributes or sequence items. Let's start with the latter.

  * When used as plain `:namespace` (`:ns`) the argument signals that the element must take both the prefix and the default from its declarant type object. Make sure you get it right: not from object, not from `xml-element` class, but from direct declarant type object! For example:

    ```raku
    role Recordish is xml-element( :ns<role-default> ) {
        has Str $.foo is xml-element( :namespace );
    }
    class Record is xml-element( :ns<class-default> ) does Recordish {
        has Int $.count is xml-element;
    }
    ```

    `$.foo` will always have no prefix and the default namespace set to *"role-default"*. Whereas `$.count` would continue to use the default, calculated for a `Record` instance.

    See [*manual12.raku*](../../../../examples/manual12.raku).

  * When used as plain negation `:!namespace` (`:!ns`) the argument would make its attribute or sequence item to force the default namespace rules, no matter what `:impose-ns` or `:derive` are telling us. These two have not been discussed yet, but [*manual13.raku*](../../../../examples/manual13.raku) should be pretty much clear for understanding.

  * `:namespace` can take arguments and in this case it expects:

      * One or none default namespace string

      * One or none default namespace prefix as a *True*-value [`Pair`](https://docs.raku.org/type/Pair)

      * A list of [`Str`](https://docs.raku.org/type/Str)-value pairs where keys are namespace prefixes, and the values are namespaces

    For example:

    ```raku
    :ns("default-ns", :pfx, :pfx('http://prefix.namespace', :foo('http://foo.namespace')))
    ```

    This declaration contains one default namespace *"default-ns"*, one default namespace prefix *pfx*, and definitions for prefixes *pfx*, and *foo*.

    *Note* that `LibXML::Class` does its best to preserve the order in which prefix declarations are encountered. It means that whatever entity, which used the above `:ns` declaration would have *xmlns:pfx*, and *xmlns:foo* attributes to appear in exactly this order when serialized into the final XML.

### Defined And Undefined Namespace

A namespace is considered *defined* for an entity if any of the default or prefix is set to a concrete value. For the default namespace it means that even when it is *no namespace* value, which is an empty string *""*, we consider it as defined.

### Namespaces Propagation

Default namespace and prefix definitions are propagaded from the entity, where they are declarated, downstream to the child `xml-element` entitiies. This rule doesn't apply to the default prefix. In [*manual14.raku*](../../../../examples/manual14.raku) the propagation principle is demonstrated by declarations of class `Record` and attribute `@.info` of `Meta` where both are using prefix *foo* as their default even though it is declared for `Root`.

The example is also good to see how not declaring the default namespace results in the top-level one to be used, even when a parent element is explicitly prefixed. While it might not be really apparent at the first glance, but this is to follow XML principles.

### Imposing Namespaces

Sometimes it makes sense for type object's default namespace and prefix to be used by all serializable attributes, except for `xml-text` one, of course. Apparently, one way to do it is to use boolean `:namespace` with each one, but that could be too tedious for a big number of attributes. Instead, with `:impose-ns` argument of `xml-element` trait, this could be achieved "in a single click":

```raku
role Itemish is xml-element(:impose-ns, :ns(:pfx)) {
    has Str $.attr1 is xml-element;
    has Int $.attr2 is xml-attribute;
}
class Item is xml-element(:ns("my-ns", :pfx<pfx-ns>)) does Itemish {
    has Num $.attr3 is xml-element;
}
```

This snippet is done this way to illustrate how a role can enforce its defaults while a class consuming the role would still be using its own defaults.

Imposition only applies to entities with undefined namespace.

### Deriving Namespace Defaults

When we say that an entity is *deriving* its namespace defaults it means that it takes them from its parent XML element. In other words, if we want something to *derive* then we want it to stick to the same namespace as its parent XML element.

Have you noticed the doubling of 'parent XML element'? This is because what is really takes place when defaults are computed for an attribute or sequence item, no matter wether we serialize or deserialize. In either case, when we come down to processing the entity it means that the parent element of it has been validated and its namespacing is fully established.

Derivation can be triggerred on per entity with help of `:derive` keyword, or disabled using negated version `:!derive`.

To have it as the default globally or per-document one can use corresponding setting on [`LibXML::Config`](https://modules.raku.org/dist/LibXML::Config):

```raku
LibXML::Config.global(:derive);
```

or per document:

```raku
class Root is xml-element(...) {...}
my $root = Root.new(...);
$root.to-xml( :config{:derive} );
```

Sometimes it makes sense to have derivation default on or off for only XML elements or attributes. This is done by specifying corresponding named argument with `:derive`:

```raku
$root.to-xml( :config{ :derive(:attribute) } );
$root.to-xml( :config{ :derive(:element) } );
```

The case of individual default is well covered by [*200-pml-parser.rakutest*](../../../../t/200-pml-parser.rakutest), where attributes of the source XML are all prefix-less whereas elements are prefixed. The test also makes a great explanation as to why derivation exists as such. Even though the source XML is using *xsd* prefix, but generally speaking, this choice is somewhat arbitrary and any other prefix can be used. What is not arbitrary is that the prefix is persistent and is shared among parent and children elements.

[*manual15.raku*](../../../../examples/manual15.raku) contains simpler but made up example of deriving. It also has an example of what happens when we deserialize without `:derive` a structure that's been serialized with this setting. In few words, we end up with unused elements and get warned about it (or worse, depending on config's `severity` parameter).

Derivation defaults only for entities with undefined namespace.

XML Element Based Search
------------------------

One of the features of [`LibXML`](https://modules.raku.org/dist/LibXML) module that quickly captures ones attention from the start is [XPath based search](https://github.com/libxml-raku/LibXML-raku/blob/0.10.3/docs/Node.md#method-findnodes) for XML nodes within document. Apparently, it wouldn't work for deserialized... But you guessed it already: it actually does! Because navigating a big datastructure, looking for Raku representation of a particular XML node is not easy if possible at all.

The search is implemented for both `xml-element` type objects and [`LibXML::Class::Document`](Document.md). Though the method on the type objects is following API naming guidelines and is named `xml-findnodes`, whereas on the document, which is a `LibXML::Class` thing on its own, it doesn't have the prefix and is simply named `findnodes`. Otherwise both are eventually end up calling document's `find-deserialization` method.

Searching for deserialization is, in a way, rather simple process of mapping XML node unique key, provided by the `unique-key` method of `LibXML::Node`, into a Raku object produced from that node. Since `LibXML::Node` `findnodes` method returns an iterable, `LibXML::Class` searches would come up with a [`Seq`](https://docs.raku.org/type/Seq) of found deserializations.

Note, please, how the term 'Raku object' adove is used without any reference to `LibXML::Class`. This is because deserialization of a node might result an anything, ranging from basic type value to whatever user-provided deserializer returned.

Another important note to be made is that the permanent reference to 'deserialization' is not accidental and it means that search only works for `xml-element`-instances produced from some kind of XML source.

So far so good, but let's get back to that infamous "in a way" saying above. Not even to mention that deserializations are often lazy by default because this is not your problem and `LibXML::Class` is seemingly succeeds in solving it for you. But there are unapparent surprises like XML contaners (`:container` declaration), arrays, sequences, XML attributes...

Luckily, there is one simple rule to remember about all of them: if an XML node has been used to produces a deserialization then search would return that deserialized value. Most simple example would be an XML container element mapping into the attribute value, for which it was declared. A container's child element would map either into the same attribute, or into it's respective array element, etc.

Another case would be a text node. Since there could only be single `xml-text` attribute on a class, even if an XML element contains multiple `#text` they all would result in the same attribute value.

### Disabling Or Tuning The Search Feature

If you are not interested in searching and would like to spare some memory instead then consider disabling `deserialization-registry` on configuration object:

```raku
LibXML::Config.global( :disable-registry );
# ... or ...
$root.from-xml: ..., :config{ :disable-registry };
```

Another potentially memory-hungry feature is the *global index* of deserializations held by the document instance. Even though it only contains `xml-element` objects, a big XML structure might end up having too many of them. The global index is actually just a cache to speed up search. For this reason it can be safely disabled. In this case the search would fall back to tree traversal approach, starting from the document root and going down its attributes. Even though it is not as fast as a cache lookup, it's still good enough and takes less memory:

```raku
LibXML::Config.global( :!global-index );
# ... or ...
$root.from-xml: ..., :config{ :!global-index };
```

### Caveats

  * A search is always resulting in a `Seq`, even if it is done for a single XML node. Moreover, there are chances that the `Seq` would consists of more than a single object! This is because a deserialization can be, for example, cloned. For a basic time cloning the value do nothing, but an `xml-element` takes care of registering itself as a candidate.

  * Be careful when searching for an unused element or its child nodes of any kind as this is an error situation.

CONTRIBUTING
============

Please, submit bugs or pull requests to https://github.com/vrurg/raku-LibXML-Class. Any help would be appreciated!

SEE ALSO
========

  * [*README*](../../../../README.md)

More technical details and some insights on the implementation can be found in module documentations:

  * [`LibXML::Class`](../Class.md)

  * [`LibXML::Class::Config`](Config.md)

  * [`LibXML::Class::Document`](Document.md)

  * [`LibXML::Class::Attr`](Attr.md)

  * [`LibXML::Class::ItemDescriptor`](ItemDescriptor.md)

  * [`LibXML::Class::NS`](NS.md)

  * [`LibXML::Class::Node`](Node.md)

COPYRIGHT
=========

(c) 2023, Vadim Belman <vrurg@cpan.org>

LICENSE
=======

Artistic License 2.0

See the [*LICENSE*](../../../../LICENSE) file in this distribution.



