AXON is eXtended Object Notation. It's a simple notation of objects, documents and data. It's also a text based serialization format in first place. It tries to combine the best of JSON, XML and YAML.

pyaxon is reference implementation of the library for processing AXON with python.


In [1]:
from __future__ import print_function
from axon import loads, dumps
from pprint import pprint

There are two API functions loads/dumps for loading/dumping from/to unicode string.

By default loading and dumping are safe. By the word "safe" we mean that there is no user code is executed while loading and dumping. Unicode strings are converted only into python objects of given types. There is "unsafe" mode too. It allows to transform unicode string into user defined objects and to dump objects into unicode string under user control. But this is the topic of another post.

Simple example


In [2]:
text = '''\
note {
   from: "Pooh"
   to: "Bee"
   posted: 2006-08-15T17:30
   heading: "Honey"
   "Don't forget to get me honey!" }
'''
vals = loads(text)

Here vals is always list of objects.


In [3]:
ob = vals[0]
type(ob)


Out[3]:
axon._objects.Node

We see that the message is converted to the instance of class Element. Attribute vals.mapping is dictionary containing objects's attributes:


In [5]:
print(type(ob.__attrs__))
print(ob.__attrs__)


<class 'axon.odict.OrderedDict'>
OrderedDict([('from', 'Pooh'), ('to', 'Bee'), ('posted', datetime.datetime(2006, 8, 15, 17, 30)), ('heading', 'Honey')])

Attributes of the object are accessable by methods get/set:


In [10]:
[(attr, getattr(ob, attr)) for attr in ob.__attrs__]


Out[10]:
[('from', 'Pooh'),
 ('to', 'Bee'),
 ('posted', datetime.datetime(2006, 8, 15, 17, 30)),
 ('heading', 'Honey')]

Element objects has content - list of values. They are accessible by python's sequence protocol. In our case the first value is the message body of the note.


In [11]:
print(ob[0])


Don't forget to get me honey!

For dumping objects there are three modes. First mode is compact:


In [12]:
print(dumps([ob]))


note{from:"Pooh" to:"Bee" posted:2006-08-15T17:30 heading:"Honey" "Don't forget to get me honey!"}

Second mode is pretty dumping mode with indentations and without braces:


In [13]:
print(dumps([ob], pretty=1))


note
  from: "Pooh"
  to: "Bee"
  posted: 2006-08-15T17:30
  heading: "Honey"
  "Don't forget to get me honey!"

Third mode is pretty dumping mode with indentation and braces:


In [14]:
print(dumps([ob], pretty=1, braces=1))


note {
  from: "Pooh"
  to: "Bee"
  posted: 2006-08-15T17:30
  heading: "Honey"
  "Don't forget to get me honey!"}

At the end let's consider JSON-like representation too:


In [16]:
text = """\
{note: {
    from: "Pooh"
    to: "Bee"
    posted: 2006-08-15T17:30
    heading: "Honey"
    body: "Don't forget to get me honey!"
}}
"""
vals = loads(text)

It has converted into python dicts:


In [17]:
pprint(vals)


[{'note': {'body': "Don't forget to get me honey!",
           'from': 'Pooh',
           'heading': 'Honey',
           'posted': datetime.datetime(2006, 8, 15, 17, 30),
           'to': 'Bee'}}]

Compact dump is pretty small in size.


In [18]:
print(dumps(vals))


{note:{body:"Don't forget to get me honey!" from:"Pooh" heading:"Honey" posted:2006-08-15T17:30 to:"Bee"}}

Dumping in pretty mode is also pretty formatted.


In [19]:
print(dumps(vals, pretty=1))


{note: {
    body: "Don't forget to get me honey!"
    from: "Pooh"
    heading: "Honey"
    posted: 2006-08-15T17:30
    to: "Bee"}}

JSON-like objects are pretty dumps only in indented form with braces.

JSON-like example

Let's consider now JSON-like example with crossreferences and datetimes:


In [20]:
text = '''\
{
  topic: [
     &1 {python: "Python related"}
     &2 {axon: "AXON related"}
     &3 {json: "JSON related"}
  ]
  posts: [
      { id: 1
        topic: *1
        date: 2012-01-02T12:15+03 
        body:"..." }
      { id: 2
        topic: *2
        date: 2012-01-12T09:25+03 
        body:"..." }
      { id: 3
        topic: *3
        date: 2012-02-08T10:35+03 
        body:"..." }
  ]
}
'''
vals = loads(text)
pprint(vals)


[{'posts': [{'body': '...',
             'date': datetime.datetime(2012, 1, 2, 12, 15, tzinfo=datetime.timezone(datetime.timedelta(0, 10800))),
             'id': 1,
             'topic': {'python': 'Python related'}},
            {'body': '...',
             'date': datetime.datetime(2012, 1, 12, 9, 25, tzinfo=datetime.timezone(datetime.timedelta(0, 10800))),
             'id': 2,
             'topic': {'axon': 'AXON related'}},
            {'body': '...',
             'date': datetime.datetime(2012, 2, 8, 10, 35, tzinfo=datetime.timezone(datetime.timedelta(0, 10800))),
             'id': 3,
             'topic': {'json': 'JSON related'}}],
  'topic': [{'python': 'Python related'},
            {'axon': 'AXON related'},
            {'json': 'JSON related'}]}]

It's easy to see that crossreference links just works:


In [21]:
assert vals[0]['topic'][0] is vals[0]['posts'][0]['topic']
assert vals[0]['topic'][1] is vals[0]['posts'][1]['topic']
assert vals[0]['topic'][2] is vals[0]['posts'][2]['topic']

Pretty dump looks like this one:


In [22]:
print(dumps(vals, pretty=1, crossref=1, sorted=0))


{ posts: [
    { topic: &1 {python: "Python related"}
      id: 1
      body: "..."
      date: 2012-01-02T12:15+03}
    { topic: &3 {axon: "AXON related"}
      id: 2
      body: "..."
      date: 2012-01-12T09:25+03}
    { topic: &2 {json: "JSON related"}
      id: 3
      body: "..."
      date: 2012-02-08T10:35+03}]
  topic: [*1 *3 *2]}

Note that sorted parameter defines whether to sort keys in dict.

XML-like example


In [23]:
text = '''\
html {
   xmlns:"http://www.w3.org/1999/xhtml"
   head {
     title {"Form Example"}
     link {
        rel:"stylesheet"
        href: "formstyle.css"
        type: "text/css" }}
   body {
      h1 {"Form Example"}
      form { 
         action: "sample.py"
         div {
             class: "formin" 
             "(a)"
             input {type:"text" name:"text1" value:"A textbox"}}
         div {
             class: "formin"
             "(b)"
             input {type:"text" size:6 maxlength:10 name:"text2"}}
         div {
             class: "formb"
             "(c)"
             input {type:"submit" value:"Go!"}}
      }
   }
}
'''
vals = loads(text)
val = vals[0]
print(val)


html{xmlns: 'http://www.w3.org/1999/xhtml' head{title{'Form Example'}, link{rel: 'stylesheet', href: 'formstyle.css', type: 'text/css'}}, body{h1{'Form Example'}, form{action: 'sample.py' div{class: 'formin' '(a)', input{type: 'text', name: 'text1', value: 'A textbox'}}, div{class: 'formin' '(b)', input{type: 'text', size: 6, maxlength: 10, name: 'text2'}}, div{class: 'formb' '(c)', input{type: 'submit', value: 'Go!'}}}}}

Let's examine the value:


In [25]:
print(type(val))
print(val.__attrs__)
head, body = val[0], val[1]


<class 'axon._objects.Node'>
OrderedDict([('xmlns', 'http://www.w3.org/1999/xhtml')])

In [26]:
print(type(head))
title, link = head
print(title)
print(link)


<class 'axon._objects.Node'>
title{'Form Example'}
link{rel: 'stylesheet', href: 'formstyle.css', type: 'text/css'}

In [36]:
h1, form = body
print(h1)


h1{'Form Example'}

In [37]:
print(form.__vals__)
div1, div2, div3 = form


[div{class: 'formin' '(a)', input{type: 'text', name: 'text1', value: 'A textbox'}}, div{class: 'formin' '(b)', input{type: 'text', size: 6, maxlength: 10, name: 'text2'}}, div{class: 'formb' '(c)', input{type: 'submit', value: 'Go!'}}]

In [38]:
print(div1.__attrs__)
label1, input1 = div1
print(label1)
print(input1)


OrderedDict([('class', 'formin')])
(a)
input{type: 'text', name: 'text1', value: 'A textbox'}

In [40]:
print(div2.__attrs__)
label2, input2 = div2
print(label2)
print(input2)


OrderedDict([('class', 'formin')])
(b)
input{type: 'text', size: 6, maxlength: 10, name: 'text2'}

In [41]:
print(type(div3))
print(div3.__attrs__)
label3, input3 = div3
print(label3)
print(input3)


<class 'axon._objects.Node'>
OrderedDict([('class', 'formb')])
(c)
input{type: 'submit', value: 'Go!'}

In [42]:
print(dumps([val], pretty=1))


html
  xmlns: "http://www.w3.org/1999/xhtml"
  head
    title
      "Form Example"
    link
      rel: "stylesheet"
      href: "formstyle.css"
      type: "text/css"
  body
    h1
      "Form Example"
    form
      action: "sample.py"
      div
        class: "formin"
        "(a)"
        input
          type: "text"
          name: "text1"
          value: "A textbox"
      div
        class: "formin"
        "(b)"
        input
          type: "text"
          size: 6
          maxlength: 10
          name: "text2"
      div
        class: "formb"
        "(c)"
        input
          type: "submit"
          value: "Go!"

In [43]:
print(dumps([val], pretty=1, braces=1))


html {
  xmlns: "http://www.w3.org/1999/xhtml"
  head {
    title {"Form Example"}
    link {
      rel: "stylesheet"
      href: "formstyle.css"
      type: "text/css"}}
  body {
    h1 {"Form Example"}
    form {
      action: "sample.py"
      div {
        class: "formin"
        "(a)"
        input {
          type: "text"
          name: "text1"
          value: "A textbox"}}
      div {
        class: "formin"
        "(b)"
        input {
          type: "text"
          size: 6
          maxlength: 10
          name: "text2"}}
      div {
        class: "formb"
        "(c)"
        input {
          type: "submit"
          value: "Go!"}}}}}

Dataset example

Let's consider simple tabular dataset:


In [44]:
text = '''\
dataset {
   fields: ("id" "date" "time" "territory_id" "A" "B" "C")
   (1 2012-01-10 12:35 17 3.14 22 33500)
   (2 2012-01-11 13:05 27 1.25 32 11500)
   (3 2012-01-12 10:45 -17 -2.26 -12 44700)
}
'''
ob = loads(text)[0]
print(ob.__tag__)
pprint(ob.__attrs__)
pprint(ob.__vals__, width=132)
print("\nPretty form of dataset:")
print(dumps([ob], pretty=1, hsize=10))


dataset
OrderedDict([('fields', ('id', 'date', 'time', 'territory_id', 'A', 'B', 'C'))])
[(1, datetime.date(2012, 1, 10), datetime.time(12, 35), 17, 3.14, 22, 33500),
 (2, datetime.date(2012, 1, 11), datetime.time(13, 5), 27, 1.25, 32, 11500),
 (3, datetime.date(2012, 1, 12), datetime.time(10, 45), -17, -2.26, -12, 44700)]

Pretty form of dataset:
dataset
  fields: ("id" "date" "time" "territory_id" "A" "B" "C")
  (1 2012-01-10 12:35 17 3.14 22 33500)
  (2 2012-01-11 13:05 27 1.25 32 11500)
  (3 2012-01-12 10:45 -17 -2.26 -12 44700)

In [47]:
from collections import namedtuple
Datarow = namedtuple("Datarow", ob.fields)
rows = []
for line in ob:
    print(type(line), line)
    rows.append(Datarow(*line))
print("\n")
for row in rows:
    print(type(row), row)


<class 'tuple'> (1, datetime.date(2012, 1, 10), datetime.time(12, 35), 17, 3.14, 22, 33500)
<class 'tuple'> (2, datetime.date(2012, 1, 11), datetime.time(13, 5), 27, 1.25, 32, 11500)
<class 'tuple'> (3, datetime.date(2012, 1, 12), datetime.time(10, 45), -17, -2.26, -12, 44700)


<class '__main__.Datarow'> Datarow(id=1, date=datetime.date(2012, 1, 10), time=datetime.time(12, 35), territory_id=17, A=3.14, B=22, C=33500)
<class '__main__.Datarow'> Datarow(id=2, date=datetime.date(2012, 1, 11), time=datetime.time(13, 5), territory_id=27, A=1.25, B=32, C=11500)
<class '__main__.Datarow'> Datarow(id=3, date=datetime.date(2012, 1, 12), time=datetime.time(10, 45), territory_id=-17, A=-2.26, B=-12, C=44700)

In [ ]: