This is a pre-production deployment of Warehouse. Changes made here affect the production instance of PyPI (pypi.python.org).
Help us improve Python packaging - Donate today!
Project Description

Schema To XML

Introduction

This package can convert objects described by Zope 3 schema to simple XML structures. It’s also able to convert this XML back into objects. The export and import processes are completely schema-driven; any attribute not described in the schema is not seen by this system at all.

This system can be used to create export and import systems for Zope 3 applications. It could also be used to provide XML representations of objects for other purposes, such as XSLT transformations, or even just to get a full-text representation for index purposes.

The package lies on lxml for the serialization to XML.

Serialization

Let’s first define a simple Zope 3 schema:

>>> from zope import interface, schema
>>> class IName(interface.Interface):
...     first_name = schema.TextLine(title=u'First name')
...     last_name = schema.TextLine(title=u'Last name')

Let’s now make a class that implements this schema:

>>> from zope.interface import implements
>>> class Name(object):
...     implements(IName)
...     def __init__(self, first_name, last_name):
...         self.first_name = first_name
...         self.last_name = last_name

Let’s make an instance of the class:

>>> name = Name('Karel', 'Titulaer')

Now let’s serialize it to XML:

>>> from z3c.schema2xml import serialize
>>> print serialize('container', IName, name)
<container>
  <first_name>Karel</first_name>
  <last_name>Titulaer</last_name>
</container>

This also works for other kinds of fields:

>>> from zope import interface, schema
>>> class IAddress(interface.Interface):
...     street_name = schema.TextLine(title=u'Street name')
...     number = schema.Int(title=u'House number')
>>> class Address(object):
...     implements(IAddress)
...     def __init__(self, street_name, number):
...         self.street_name = street_name
...         self.number = number
>>> address = Address('Hofplein', 42)
>>> print serialize('container', IAddress, address)
<container>
  <street_name>Hofplein</street_name>
  <number>42</number>
</container>

If a field is not filled in, the serialization will result in an empty element:

>>> address2 = Address(None, None)
>>> print serialize('container', IAddress, address2)
<container>
  <street_name/>
  <number/>
</container>

If a schema defines an Object field with its own schema, the serialization can also handle this:

>>> class IPerson(interface.Interface):
...     name = schema.Object(title=u"Name", schema=IName)
...     address = schema.Object(title=u"Address", schema=IAddress)

>>> class Person(object):
...     implements(IPerson)
...     def __init__(self, name, address):
...         self.name = name
...         self.address = address

>>> person = Person(name, address)
>>> print serialize('person', IPerson, person)
<person>
  <name>
    <first_name>Karel</first_name>
    <last_name>Titulaer</last_name>
  </name>
  <address>
    <street_name>Hofplein</street_name>
    <number>42</number>
  </address>
</person>

A schema can also define a List field with elements with their own schema. Let’s make an object and serialize it:

>>> class ICommission(interface.Interface):
...     members = schema.List(
...         title=u"Commission",
...         value_type=schema.Object(__name__='person',
...         schema=IPerson))

Note that we have to explicitly specify __name__ for the field that’s used for value_type here, otherwise we have no name to serialize to XML with.

>>> class Commission(object):
...     implements(ICommission)
...     def __init__(self, members):
...         self.members = members
>>> commission = Commission(
...     [person, Person(Name('Chriet', 'Titulaer'), Address('Ruimteweg', 3))])
>>> print serialize('commission', ICommission, commission)
<commission>
  <members>
    <person>
      <name>
        <first_name>Karel</first_name>
        <last_name>Titulaer</last_name>
      </name>
      <address>
        <street_name>Hofplein</street_name>
        <number>42</number>
      </address>
    </person>
    <person>
      <name>
        <first_name>Chriet</first_name>
        <last_name>Titulaer</last_name>
      </name>
      <address>
        <street_name>Ruimteweg</street_name>
        <number>3</number>
      </address>
    </person>
  </members>
</commission>

We get an adapter lookop failure whenever we try to serialize a field type for which there’s no an serializer:

>>> class IWithNonSerializableField(interface.Interface):
...     field = schema.Field(title=u"Commission")
>>> class NotSerializable(object):
...     implements(IWithNonSerializableField)
...     def __init__(self, value):
...         self.field = value
>>> not_serializable = NotSerializable(None)
>>> serialize('noway', IWithNonSerializableField, not_serializable)
Traceback (most recent call last):
 ...
TypeError: ('Could not adapt', <zope.schema._bootstrapfields.Field object at ...>, <InterfaceClass z3c.schema2xml._schema2xml.IXMLGenerator>)

Deserialization

Now we want to deserialize XML according to a schema to an object that provides this schema.

>>> from z3c.schema2xml import deserialize
>>> xml = '''
...  <container>
...    <first_name>Karel</first_name>
...    <last_name>Titulaer</last_name>
...  </container>
...  '''
>>> name = Name('', '')
>>> deserialize(xml, IName, name)
>>> name.first_name
u'Karel'
>>> name.last_name
u'Titulaer'

The order of the fields in XML does not matter:

>>> xml = '''
...  <container>
...    <last_name>Titulaer</last_name>
...    <first_name>Karel</first_name>
...  </container>
...  '''
>>> name = Name('', '')
>>> deserialize(xml, IName, name)
>>> name.first_name
u'Karel'
>>> name.last_name
u'Titulaer'

After deserialization, the object alsoProvides the schema interface:

>>> IName.providedBy(name)
True

This also works for other kinds of fields:

>>> xml = '''
...  <container>
...    <street_name>Hofplein</street_name>
...    <number>42</number>
...  </container>
...  '''
>>> address = Address('', 0)
>>> deserialize(xml, IAddress, address)
>>> address.street_name
u'Hofplein'
>>> address.number
42

If a schema defines an Object field with its own schema, the serialization can also handle this:

>>> xml = '''
...  <person>
...    <name>
...      <first_name>Karel</first_name>
...      <last_name>Titulaer</last_name>
...    </name>
...    <address>
...      <street_name>Hofplein</street_name>
...      <number>42</number>
...    </address>
...  </person>
...  '''
>>> person = Person(Name('', ''), Address('', 0))
>>> deserialize(xml, IPerson, person)
>>> person.name.first_name
u'Karel'
>>> person.name.last_name
u'Titulaer'
>>> person.address.street_name
u'Hofplein'
>>> person.address.number
42
>>> IPerson.providedBy(person)
True
>>> IName.providedBy(person.name)
True
>>> IAddress.providedBy(person.address)
True

Again the order in which the fields come in XML shouldn’t matter:

>>> xml = '''
...  <person>
...    <address>
...      <number>42</number>
...      <street_name>Hofplein</street_name>
...    </address>
...    <name>
...      <last_name>Titulaer</last_name>
...      <first_name>Karel</first_name>
...    </name>
...  </person>
...  '''
>>> person = Person(Name('', ''), Address('', 0))
>>> deserialize(xml, IPerson, person)
>>> person.name.first_name
u'Karel'
>>> person.name.last_name
u'Titulaer'
>>> person.address.street_name
u'Hofplein'
>>> person.address.number
42
>>> IPerson.providedBy(person)
True
>>> IName.providedBy(person.name)
True
>>> IAddress.providedBy(person.address)
True

>>> xml = '''
... <commission>
...  <members>
...    <person>
...      <name>
...        <first_name>Karel</first_name>
...        <last_name>Titulaer</last_name>
...      </name>
...      <address>
...        <street_name>Hofplein</street_name>
...        <number>42</number>
...      </address>
...    </person>
...    <person>
...      <name>
...        <first_name>Chriet</first_name>
...        <last_name>Titulaer</last_name>
...     </name>
...      <address>
...        <street_name>Ruimteweg</street_name>
...        <number>3</number>
...      </address>
...    </person>
...  </members>
... </commission>
... '''

>>> commission = Commission([])
>>> deserialize(xml, ICommission, commission)
>>> len(commission.members)
2
>>> member = commission.members[0]
>>> member.name.first_name
u'Karel'
>>> member.address.street_name
u'Hofplein'
>>> member = commission.members[1]
>>> member.name.first_name
u'Chriet'
>>> member.address.street_name
u'Ruimteweg'

Whenever the XML element is empty, the resulting value should be None:

>>> from z3c.schema2xml import deserialize
>>> xml = '''
...  <container>
...    <first_name></first_name>
...    <last_name/>
...  </container>
...  '''
>>> name = Name('', '')
>>> deserialize(xml, IName, name)
>>> name.first_name is None
True
>>> name.last_name is None
True

For all kinds of fields, like strings and ints…:

>>> xml = '''
...  <container>
...    <street_name/>
...    <number/>
...  </container>
...  '''
>>> address = Address('', 0)
>>> deserialize(xml, IAddress, address)
>>> address.street_name is None
True
>>> address.number is None
True

…and the fields of subobjects (but not the subobject themselves!):

>>> xml = '''
...  <person>
...    <name>
...      <first_name/>
...      <last_name/>
...    </name>
...    <address>
...      <street_name/>
...      <number/>
...    </address>
...  </person>
...  '''
>>> person = Person(Name('', ''), Address('', 0))
>>> deserialize(xml, IPerson, person)
>>> person.name.first_name is None
True
>>> person.name.last_name is None
True
>>> IPerson.providedBy(person)
True
>>> IName.providedBy(person.name)
True
>>> person.address is None
False
>>> person.address.street_name is None
True
>>> person.address.number is None
True
>>> IAddress.providedBy(person.address)
True

Similarly, where a sequence is expected the value should be an empty sequence:

>>> xml = '''
... <commission>
...   <members/>
... </commission>
... '''
>>> commission = Commission([])
>>> deserialize(xml, ICommission, commission)
>>> len(commission.members)
0

TextLine, Int, Object and List have just been tested. Now follow tests for the other field types that have a serializer.

Datetime

Datetime objects:

>>> from datetime import datetime
>>> class IWithDatetime(interface.Interface):
...     datetime = schema.Datetime(title=u'Date and time')
>>> class WithDatetime(object):
...     implements(IWithDatetime)
...     def __init__(self, datetime):
...         self.datetime = datetime
>>> with_datetime = WithDatetime(datetime(2006, 12, 31))
>>> xml = serialize('container', IWithDatetime, with_datetime)
>>> print xml
<container>
  <datetime>2006-12-31T00:00:00</datetime>
</container>
>>> new_datetime = WithDatetime(None)
>>> deserialize(xml, IWithDatetime, new_datetime)
>>> new_datetime.datetime.year
2006
>>> new_datetime.datetime.month
12
>>> new_datetime.datetime.day
31

Let’s try it with the field not filled in:

>>> with_datetime = WithDatetime(None)
>>> xml = serialize('container', IWithDatetime, with_datetime)
>>> print xml
<container>
  <datetime/>
</container>
>>> new_datetime= WithDatetime(None)
>>> deserialize(xml, IWithDatetime, new_datetime)
>>> new_datetime.datetime is None
True

Choice

Choice fields. For now, we only work with Choice fields that have text values:

>>> from zc.sourcefactory.basic import BasicSourceFactory
>>> class ChoiceSource(BasicSourceFactory):
...     def getValues(self):
...         return [u'alpha', u'beta']
>>> class IWithChoice(interface.Interface):
...     choice = schema.Choice(title=u'Choice', required=False,
...                            source=ChoiceSource())
>>> class WithChoice(object):
...     implements(IWithChoice)
...     def __init__(self, choice):
...         self.choice = choice
>>> with_choice = WithChoice('alpha')
>>> xml = serialize('container', IWithChoice, with_choice)
>>> print xml
<container>
  <choice>alpha</choice>
</container>
>>> new_choice = WithChoice(None)
>>> deserialize(xml, IWithChoice, new_choice)
>>> new_choice.choice
'alpha'
>>> with_choice = WithChoice(None)
>>> xml = serialize('container', IWithChoice, with_choice)
>>> print xml
<container>
  <choice/>
</container>
>>> deserialize(xml, IWithChoice, new_choice)
>>> new_choice.choice is None
True

Set

Set fields are very similar to List fields:

>>> class IWithSet(interface.Interface):
...     set = schema.Set(title=u'Set', required=False,
...                      value_type=schema.Choice(__name__='choice',
...                                               source=ChoiceSource()))
>>> class WithSet(object):
...     implements(IWithSet)
...     def __init__(self, set):
...         self.set = set
>>> with_set = WithSet(set(['alpha']))
>>> xml = serialize('container', IWithSet, with_set)
>>> print xml
<container>
  <set>
    <choice>alpha</choice>
  </set>
</container>
>>> with_set = WithSet(set(['alpha', 'beta']))
>>> xml = serialize('container', IWithSet, with_set)
>>> print xml
<container>
  <set>
    <choice>alpha</choice>
    <choice>beta</choice>
  </set>
</container>
>>> new_set = WithSet(None)
>>> deserialize(xml, IWithSet, new_set)
>>> new_set.set
set(['alpha', 'beta'])

CHANGES

1.0 (2008-12-05)

  • Changed dependency on grokcore.component so that this becomes useful in straight Zope 3 applications as well.
  • Run tests against lxml 2.0.9.

0.10 (2008-03-10)

  • First checkin in svn.zope.org.

Download

Release History

Release History

1.0

This version

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

Download Files

Download Files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

File Name & Checksum SHA256 Checksum Help Version File Type Upload Date
z3c.schema2xml-1.0.tar.gz (12.5 kB) Copy SHA256 Checksum SHA256 Source Dec 5, 2008

Supported By

WebFaction WebFaction Technical Writing Elastic Elastic Search Pingdom Pingdom Monitoring Dyn Dyn DNS Sentry Sentry Error Logging CloudAMQP CloudAMQP RabbitMQ Heroku Heroku PaaS Kabu Creative Kabu Creative UX & Design Fastly Fastly CDN DigiCert DigiCert EV Certificate Rackspace Rackspace Cloud Servers DreamHost DreamHost Log Hosting