attrs library integrationΒΆ

Since paxb is based on attrs library paxb and attrs APIs can be mixed together.

Decorator paxb.model() accepts attr.s() function arguments as **kwargs and internally passes them to it. For example you can pass str=True argument to ask attrs library to generate __str__ method for a class.

Functions paxb.attr(), paxb.field() and paxb.nested() accept attr.ib() function arguments as **kwargs and internally passes them to it. For example you can pass default or factory argument to set a default value for a class field or converter argument to convert a value to an appropriate type. Look at the example:

>>> import paxb as pb
>>>
>>> @pb.model
... class Model:
...     field = pb.field(default='1', converter=int)
...
>>> Model()
Model(field=1)

paxb in conjuction with attrs library can be used as a flexible xml-to-json converter and vise versa. All you need is just to declare a model, fields and field types, the rest is up to paxb.

Suppose you have an xml document user.xml:

<?xml version="1.0" encoding="utf-8"?>
<doc:envelope xmlns:doc="http://www.test1.org">
    <doc:user name="Alex" surname="Ivanov" age="26">

        <doc:contacts>
            <doc:phone>+79204563539</doc:phone>
            <doc:email>alex@gmail.com</doc:email>
            <doc:email>alex@mail.ru</doc:email>
        </doc:contacts>

        <data:occupations xmlns:data="http://www.test2.org">
            <data:occupation title="yandex">
                <data:address>Moscow</data:address>
                <data:employees>8854</data:employees>
            </data:occupation>
            <data:occupation title="skbkontur">
                <data:address>Yekaterinburg</data:address>
                <data:employees>7742</data:employees>
            </data:occupation>
        </data:occupations>

    </doc:user>
</doc:envelope>

First you need to describe models. Then deserialize the document to an object and call attr.asdict() attrs library API method:

import json
import attr
import paxb as pb

xml = '''<?xml version="1.0" encoding="utf-8"?>
<doc:envelope xmlns:doc="http://www.test1.org">
    <doc:user name="Alex" surname="Ivanov" age="26">

        <doc:contacts>
            <doc:phone>+79204563539</doc:phone>
            <doc:email>alex@gmail.com</doc:email>
            <doc:email>alex@mail.ru</doc:email>
        </doc:contacts>

        <data:occupations xmlns:data="http://www.test2.org">
            <data:occupation title="yandex">
                <data:address>Moscow</data:address>
                <data:employees>8854</data:employees>
            </data:occupation>
            <data:occupation title="skbkontur">
                <data:address>Yekaterinburg</data:address>
                <data:employees>7742</data:employees>
            </data:occupation>
        </data:occupations>

    </doc:user>
</doc:envelope>
'''

@pb.model(name="occupation")
class Occupation:
    title = pb.attribute()
    address = pb.field()
    employees = pb.field(converter=int)

@pb.model(name="user", ns="doc")
class User:
    name = pb.attribute()
    surname = pb.attribute()
    age = pb.attribute(converter=int)

    phone = pb.wrap("contacts", pb.field())
    emails = pb.wrap("contacts", pb.as_list(pb.field(name="email")))

    occupations = pb.wrap("occupations", pb.lst(pb.nested(Occupation)), ns="data")

user = pb.from_xml(User, xml, envelope="doc:envelope", ns_map={
    "doc": "http://www.test1.org",
    "data": "http://www.test2.org",
})

print(json.dumps(attr.asdict(user), indent=4))

Output:

{
    "name": "Alex",
    "surname": "Ivanov",
    "age": 26,
    "phone": "+79204563539",
    "emails": [
        "alex@gmail.com",
        "alex@mail.ru"
    ],
    "occupations": [
        {
            "title": "yandex",
            "address": "Moscow",
            "employees": 8854
        },
        {
            "title": "skbkontur",
            "address": "Yekaterinburg",
            "employees": 7742
        }
    ]
}