Mapping XML to Models

XML is mapped to xml_models.Model via Fields. Each field requires an xpath expression that determines which node or attribute to get the data from. Each field also has an optional default value for when no value can be retrieved from the XML.

Basic Fields

The available field mappings are

  • CharField – returns string data
  • IntField – returns integers
  • DateField – returns a date from using the supplied date_format mask or the default ISO8601 format
  • FloatField – returns a floating point number
  • BoolField – returns a boolean
  • OneToOneField – returns a xml_model.Model subclass
  • CollectionField – returns a collection of either one of the above types, or an xml_model.Model subclass

Most of these fields are fairly self explanatory. The CollectionField and OneToOneField is where it gets interesting. This is what allows you to map instances or collections of nested entities, such as:-

<Person id="112">
  <firstName>Chris</firstName>
  <lastName>Tarttelin</lastName>
  <occupation>Code Geek</occupation>
  <website>http://www.pyruby.com</website>
  <contact-info>
    <contact type="telephone">
      <info>(555) 555-5555</info>
      <description>Cell phone, but no calls during work hours</description>
    </contact>
    <contact type="email">
      <info>me@here.net</info>
      <description>Where possible, contact me by email</description>
    </contact>
    <contact type="telephone">
      <info>1-800-555-5555</info>
      <description>Toll free work number for during office hours.</description>
    </contact>
  </contact-info>
</Person>

This can be mapped using a Person and a ContactInfo model:-

class Person(Model):
  id = IntField(xpath="/Person/@id")
  firstName = CharField(xpath="/Person/firstName")
  lastName = CharField(xpath="/Person/lastName")
  contacts = CollectionField(ContactInfo, order_by="contact_type", xpath="/Person/contact-info/contact")

class ContactInfo(Model):
  contact_type = CharField(xpath="/contact/@type")
  info = CharField(xpath="/contact/info")
  description = CharField(xpath="/contact/description", default="No description supplied")

This leads to the usage of a person as :-

>>> person.contacts[0].info
me@here.com

Collections

When querying collections or lists, it is assumed that a collection of zero or more results are returned wrapped in an enclosing collection tag.

As some REST APIs may return lists wrapped in one or more layers of metadata, Models may also define a collection_node attribute. this allows the XML processor to find the relevant node.

Note

collection_node is the tag name only and not an xpath expression.

For example, given the following XML

<reponse status="200">
  <metadata count="2">
  <collection>
    <model ... />
    <model ... />
  </collection>
  </metadata>
</response>

We would need to define a Model with a collection_node like so

class SomeModel(Model):
  fieldA = CharField(xpath="/some/node")

  collection_node = 'collection'

Nested Collections

Similarly with Collections there may be a need where you have collections nested in metadata objects that are not relevant.

For example, given the following XML, you may only be interested in the Models. Rather than having to create a Collection model as well you can create a collection from the nested XML using the collection_xpath attribute.

<reponse status="200">
  <metadata count="2">
  <collection name="Collection1">
    <model ... />
    <model ... />
  </collection>
  <collection name="Collection2">
    <model ... />
    <model ... />
  </collection>
  </metadata>
</response>
class SomeModel(Model):
  fieldA = CharField(xpath="/model/some/node")

  collection_xpath = '//collection/model'

Note

collection_xpath will pass the enclosing tag XML to the Model. Therefore your models field definitions

should start with the last tag name in the collection_xpath as the example does with the model tag.

Note

collection_node and collection_xpath are mutually exclusive