Skip to main content

Simple XML Python Marshaller

https://travis-ci.org/josuebrunel/pysxm.svg?branch=master https://coveralls.io/repos/github/josuebrunel/pysxm/badge.svg?branch=master http://pepy.tech/badge/pysxm

pysxm is a simple and extensible xml python marshaller. It comes with two simple and basic types:

  • SimpleType

  • ComplexType

It supports py2 and py3 and uses lxml.objectify under the hood.

Installation

pip install pysxm

Quickstart

In [1]: from pysxm import ComplexType
In [2]: class Person(ComplexType):
...:     attrib = {'description': 'a random person'}
...:     def __init__(self, fname, lname):
...:         self.fname = fname
...:         self.lname = lname
...:
In [3]: person = Person('token', 'black')
In [4]: print(person)
<person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" description="a random person">
    <lname>black</lname>
    <fname>token</fname>
</person>

Let’s say, we want a different tag for our object. An attribute tagname or _tagname can be set to define the xml tag name of the object.

In [5]: class Person(ComplexType):
...:     attrib = {'description': 'a random person'}
...:     tagname = 'student'
...:     def __init__(self, fname, lname):
...:         self.fname = fname
...:         self.lname = lname
...:
In [6]: person = Person('token', 'black')
In [7]: print(person)
<student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" description="a random person">
    <lname>black</lname>
    <fname>token</fname>
</student>

A sequence or _sequence (tuple or list) attribute can be set to decide of the order or the presence of an subelement in the xml.

In [8]: class Person(ComplexType):
...:     attrib = {'description': 'a random person'}
...:     tagname = 'student'
...:     _sequence = ('city', 'fname')
...:
...:     def __init__(self, fname, lname, city):
...:         self.fname = fname
...:         self.lname = lname
...:         self.city = city
...:
In [9]: person = Person('token', 'black', 'south park')
In [10]: print(person)
<student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" description="a random person">
    <city>south park</city>
    <fname>token</fname>
</student>

Let’s add a namespace to our object.

In [11]: class Person(ComplexType):
...:     attrib = {'description': 'a random south park character'}
...:     nsmap = {'sp': 'http://southpark/xml/'}
...:
...:     def __init__(self, fname, lname, city):
...:         self.fname = fname
...:         self.lname = lname
...:         self.city = city
...:
In [12]: person = Person('token', 'black', 'south park')
In [13]: print(person)
<sp:person xmlns:sp="http://southpark/xml/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" description="a random south park character">
    <sp:lname>black</sp:lname>
    <sp:city>south park</sp:city>
    <sp:fname>token</sp:fname>
</sp:person>

Let’s make sure that a person’s group is either coon or goth. To do so, we can inherit from SimpleType object and define a restriction by overriding check_restriction(self, value) method.

In [7]: from pysxm import ComplexType, SimpleType
In [8]: class Group(SimpleType):
...:     allowed_groups = ('coon', 'goth')
...:     def check_restriction(self, value):
...:         if value not in self.allowed_groups:
...:             raise ValueError('<%s> value %s not in %s' % (self.tagname, value, self.allowed_groups))
...:
In [9]: class Person(ComplexType):
...:     def __init__(self, fname, lname, group):
...:         self.fname = fname
...:         self.lname = lname
...:         self.group = Group(group)
...:
In [10]: Person('token', 'black', 'boys')
...
<ipython-input-8-116b49042116> in check_restriction(self, value)
3     def check_restriction(self, value):
4         if value not in self.allowed_groups:
----> 5             raise ValueError('<%s> value %s not in %s' % (self.tagname, value, self.allowed_groups))
6
ValueError: <group> value boys not in ('coon', 'goth')

In [11]: print(Person('token', 'black', 'goth'))
<person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <lname>black</lname>
    <group>goth</group>
    <fname>token</fname>
</person>

Note: ComplexType can have ComplexType and SimpleType as attribute

from pysxm import ComplexType, SimpleType


class AdultAge(SimpleType):

    tagname = 'age'
    attrib = {'minvalue': '18', 'maxvalue': '100'}

    def check_restriction(self, value):
        if int(value) < 18:
            raise ValueError("<%s> '%d' < 18" % (self.tagname, value))


class Credentials(ComplexType):

    def __init__(self, login, password):
        self.login = login
        self.password = password


class Person(ComplexType):

    def __init__(self, fname, lname, credentials, age):
        self.fname = fname
        self.lname = lname
        self.credentials = Credentials(credentials['login'], credentials['password'])
        self.age = AdultAge(age)

In [3]: data = {
...:     'fname': 'token', 'lname': 'black',
...:     'credentials': {'login': 't0ken', 'password': 'l33tolite'},
...:     'age': '30'}
In [4]: person = Person(**data)
In [5]: print(person)
<person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <lname>black</lname>
    <credentials>
        <login>t0ken</login>
        <password>l33tolite</password>
    </credentials>
    <age maxvalue="100" minvalue="18">30</age>
    <fname>token</fname>
</person>
In [6]: person.save('token.xml')

The save method (object.save(<filename>)) allows you to save the xml result into a file.

In [7]: cat token.xml
<person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <lname>black</lname>
    <credentials>
        <login>t0ken</login>
        <password>l33tolite</password>
    </credentials>
    <age maxvalue="100" minvalue="18">30</age>
    <fname>token</fname>
</person>

The ext module

Pysxm comes with a couple of extended types. Those types are defined in pysxm.ext module.

DataComplexType

This is a simple DataClass of ComplexType. Here is how you can set one up:

from pysxm.ext import DataComplexType, XSimpleType


class Game(DataComplexType):
     platform = XSimpleType('platform', ['xboxone', 'xboxx'], lambda v, av: v in av)

>>> game = Game(name='state of decay 2', editor='undead labs', platform='xboxone')
>>> print(game)
<game>
    <name>state of decay 2</name>
    <platform>xboxone</platform>
    <editor>undead labs</editor>
</game>

XSimpleType

It gets tiresome to subclass a SimpleType everytime you want to check a value. To overcome that, pysxm provides a descriptor called XSimpleType:

class XSimpleType(object):

  def __init__(name=None, restriction=None, checker=None, error_msg=None, **kwargs):
    '''name: it's the name of the attribute.
    restriction: self explanatory
    checker: the fucntion that checks the input value
    error_msg: message returned when checking fails
    kwargs: as tagname, attrib or nsmap
    '''

Here is an example:

class XboxGamer(ComplexType):
    platform = XSimpleType('platform', ('xone', 'xbox360', 'xbox'), lambda v, av: v in av)
    score = XSimpleType('score', (4000, 1000000), lambda v, av: int(av[0]) <= int(v) < int(av[1]))
    lastlogin = XDateTimeType('lastlogin')

    def __init__(self, gamertag, platform, score, lastlogin):
        self.gamertag = gamertag
        self.platform = platform
        self.score = score
        self.lastlogin = lastlogin

In [1]: print(gamer_data)
{'gamertag': 'LokingHD', 'platform': 'ps4', 'score': '22526', 'lastlogin': '2018-03-21'}
In [2]: XboxGamer(**gamer_data)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-2-61f95466da46> in <module>()
----> 1 XboxGamer(**gamer_data)
/home/josue/workspace/dev/pysxdb/pysxm/ext.pyc in check(self, instance, value)
77         if not self.checker(value, self.restriction_values):
78             raise ValueError('tagname <%s> value %s is invalid: expected (%s)'
---> 79                              % (instance.tagname, value, self.restriction_values))
    80
81     def check_restriction(self, instance, value):
ValueError: tagname <xboxgamer> value ps4 is invalid: expected (('xone', 'xbox360', 'xbox'))
In [3]: gamer_data['platform'] = 'xone'
In [4]: gamer = XboxGamer(**gamer_data)
In [5]: print(gamer)
<xboxgamer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <gamertag>LokingHD</gamertag>
    <platform>xone</platform>
    <score>22526</score>
    <lastlogin>2018-03-21T00:00:00</lastlogin>
</xboxgamer>

Most of the types defined in pysxm.ext are descriptors and they’re subclassable.

Voila :wink:

Metadata

Release files for pysxm 1.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pysxm 1.5.0
File Size Uploaded
pysxm-1.5.0.tar.gz 6.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pysxm 1.5.0
File Interpreter ABI Platform
pysxm-1.5.0-py2.py3-none-any.whl Python 2, Python 3 none any Details

Total release size: 14.6 kB

Release files / pysxm-1.5.0.tar.gz

Download URL pysxm-1.5.0.tar.gz
Size 6.3 kB
Tags Source
SHA-256 checksum
How to use checksums
98c2e0f3a644d5dfd60d4829dcaf224bdebed6e1507c9427f794d689c2e9cd2a
BLAKE2b-256 checksum
How to use checksums
3b6f7ec680a2cef12c9da11912e2e04f5c5d8fa3d9bcc436a157ecc48c027cff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.7.1 CPython/3.10.12 Linux/6.5.0-15-generic

Release files / pysxm-1.5.0-py2.py3-none-any.whl

Download URL pysxm-1.5.0-py2.py3-none-any.whl
Size 8.4 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
826732fc9ed9faea4aa720fa0d4241c0bc5271cc48cdb8db2bc4b05376b5c327
BLAKE2b-256 checksum
How to use checksums
8f6f5c5059902b8b292eaef291cf3429065b01da005088f6811bef1f56e6a526
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.7.1 CPython/3.10.12 Linux/6.5.0-15-generic

Release history Release notifications | RSS feed

This release

1.5.0 This release

2 release files

1.4

1 release file

1.3

1 release file

1.2

1 release file

1.1

1 release file

1.0

1 release file

0.9

1 release file

0.8

1 release file

0.6

1 release file

0.5

1 release file

0.4

1 release file

0.3

1 release file

0.2

1 release file

0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page