In this post we will consider one advantage of AXON (see also early posts). It allows to resolve problems that arises when someone will try to translate XML to JSON. The root of this problem is in incompatibility of data models of XML and JSON. XML reprsents attributed trees with tagged nodes, but JSON represents compositions of arrays and associative arrays. This makes convertion difficult. You have to translate XML to fatty JSON (with convensions) in order to save initial structure of XML or have to reorganize initial structure in order to produce more optimal JSON. In last case inverse transformation is not possible without of losses.

Well knowing disadvantage of XML is it's verbosity. JSON usually is considered as fat free alternative to XML. let's consider example:

<person>
  <name>John Smith</name>
  <age>25</age>
  <address type="home">
     <street>21 2nd Street</street>
     <city>New York</city>
     <state>NY</state>
  </address>
  <address type="current">
     <street>1410 NE Campus Parkway</street>
     <city>Seattle</city>
     <state>WA</state>
  </address>
  <phone type="home">212-555-1234</phone>
  <phone type="fax">646-555-4567</phone>
</person>

Here is JSON alternative:

{"person": {
  "name": "John Smith",
  "age": 25,
  "address": [
    {"type": "home",
     "street": "21 2nd Street",
     "city": "New York",
     "state": "NY"
    },
    {"type": "current",
     "street": "1410 NE Campus Parkway",
     "city": "Seattle",
     "state": "WA"
    }
  ],
  "phone": [ 
    {"type": "home", "number": "212-555-1234"},
    {"type": "fax", "number": "646-555-4567"}
  ]
}}

AXON allows direct translation of XML that saves it's element/attribute structure:

person {
  name {"John Smith"}
  age {25}
  address {
     type: "home"
     street {"21 2nd Street"}
     city {"New York"}
     state {"NY"}
  }
  address { 
     type: "current"
     street {"1410 NE Campus Parkway"}
     city {"Seattle"}
     state {"WA"}
  }
  phone {type:"home" "212-555-1234"}
  phone {type:"fax" "646-555-4567"}
}

AXON representation can be built from XML one in 4 steps:

  1. Replace <tag> with tag {
  2. Replace </tag> with }
  3. Replace attr=value with attr: value
  4. Remove character , or replace it with one space character

The result of such transformation is equivalent to original XML. One can also consider it as fat free form of XML.

Let's now illustrate this feature of AXON using pyaxon package.


In [1]:
from __future__ import unicode_literals, print_function
from axon.api import loads, dumps
from axon.objects import node, attribute, Attribute, Node
from axon.objects import Builder, register_builder
from axon import dump_as_str, as_unicode, factory, reduce
from xml.etree import ElementTree
import json
from io import StringIO

There are reduce functions for ElementTree.Element and ElementTree.ElementTree types from xml.etree package. These functions will used for dumping ElementTree into AXON text.


In [2]:
@reduce(ElementTree.Element)
def element_reduce(elem):
    children = elem.getchildren()
    children = children[:]
    if elem.text and elem.text.strip():
        children.append(elem.text)
    return node(elem.tag, elem.attrib, children)
        
@reduce(ElementTree.ElementTree)
def etree_reduce(element):
    return element_reduce(element.getroot())

There is the class ElementTreeBuilder for construction of ElementTree from AXON text.


In [3]:
class ElementTreeBuilder(Builder):
    def node(self, name, attrs, vals):
        str_type = type(u'')
        if type(vals[-1]) is str_type:
            text = vals.pop(-1)
        else:
            text = None
        attribs = {}
        children = []
        if attrs:
            for name, val in attrs.items():
                attribs[name] = val
        if vals:
            for val in vals:
                children.append(val)
        e = ElementTree.Element(name, attribs)
        if children:
            e.extend(children)
        if text:
            e.text = text
        return e

Let's register ElementTree builder with name etree. This is new value for mode parameter in load/loads functions.


In [4]:
register_builder('etree', ElementTreeBuilder())

Let's consider XML text:


In [5]:
xml_text = u"""
<person>
  <name>John Smith</name>
  <age>25</age>
  <address type="home">
     <street>21 2nd Street</street>
     <city>New York</city>
     <state>NY</state>
  </address>
  <address type="current">
     <street>1410 NE Campus Parkway</street>
     <city>Seattle</city>
     <state>WA</state>
  </address>
  <phone type="home">212-555-1234</phone>
  <phone type="fax">646-555-4567</phone>
</person>
"""

Let's parse it into ElementTree that represents XML document.


In [6]:
tree = ElementTree.parse(StringIO(xml_text))
ElementTree.dump(tree)


<person>
  <name>John Smith</name>
  <age>25</age>
  <address type="home">
     <street>21 2nd Street</street>
     <city>New York</city>
     <state>NY</state>
  </address>
  <address type="current">
     <street>1410 NE Campus Parkway</street>
     <city>Seattle</city>
     <state>WA</state>
  </address>
  <phone type="home">212-555-1234</phone>
  <phone type="fax">646-555-4567</phone>
</person>

In [7]:
ElementTree.dump(tree)


<person>
  <name>John Smith</name>
  <age>25</age>
  <address type="home">
     <street>21 2nd Street</street>
     <city>New York</city>
     <state>NY</state>
  </address>
  <address type="current">
     <street>1410 NE Campus Parkway</street>
     <city>Seattle</city>
     <state>WA</state>
  </address>
  <phone type="home">212-555-1234</phone>
  <phone type="fax">646-555-4567</phone>
</person>

Here we dumping ElementTree object into AXON text.


In [8]:
axon_text = dumps([tree], pretty=1, braces=1)
print(axon_text)


person {
  name {"John Smith"}
  age {"25"}
  address {
    type: "home"
    street {"21 2nd Street"}
    city {"New York"}
    state {"NY"}}
  address {
    type: "current"
    street {"1410 NE Campus Parkway"}
    city {"Seattle"}
    state {"WA"}}
  phone {
    type: "home"
    "212-555-1234"}
  phone {
    type: "fax"
    "646-555-4567"}}

And load again from AXON text into ElementTree object:


In [9]:
xml_tree = loads(axon_text, mode='etree')[0]

In [10]:
print(xml_tree)
ElementTree.dump(xml_tree)


<Element 'person' at 0x104e0a598>
<person><name>John Smith</name><age>25</age><type type="home"><street>21 2nd Street</street><city>New York</city><state>NY</state></type><type type="current"><street>1410 NE Campus Parkway</street><city>Seattle</city><state>WA</state></type><type type="home">212-555-1234</type><type type="fax">646-555-4567</type></person>

There is AXON compact representation for comparison:


In [11]:
axon_compact_text = dumps([xml_tree], braces=1)
print(axon_compact_text)


person{name{"John Smith"} age{"25"} type{type:"home" street{"21 2nd Street"} city{"New York"} state{"NY"}} type{type:"current" street{"1410 NE Campus Parkway"} city{"Seattle"} state{"WA"}} type{type:"home" "212-555-1234"} type{type:"fax" "646-555-4567"}}

There is JSON representation for comparison too:


In [12]:
json_text = u"""
{"person": {
  "name": "John Smith",
  "age": 25,
  "address": [
    {"type": "home",
     "street": "21 2nd Street",
     "city": "New York",
     "state": "NY"
    },
    {"type": "current",
     "street": "1410 NE Campus Parkway",
     "city": "Seattle",
     "state": "WA"
    }
  ],
  "phone": [ 
    {"type": "home", "number": "212-555-1234"},
    {"type": "fax", "number": "646-555-4567"}
  ]
}}
"""
json.dumps(json.loads(json_text))


Out[12]:
'{"person": {"phone": [{"number": "212-555-1234", "type": "home"}, {"number": "646-555-4567", "type": "fax"}], "name": "John Smith", "address": [{"street": "21 2nd Street", "state": "NY", "type": "home", "city": "New York"}, {"street": "1410 NE Campus Parkway", "state": "WA", "type": "current", "city": "Seattle"}], "age": 25}}'

In [ ]:


In [ ]:


In [ ]:


In [ ]: