AXON: Tutorial

Let's import inventory for playing with AXON with python.


In [1]:
from __future__ import unicode_literals, print_function, division
from pprint import pprint
import axon
import json
import xml.etree as etree
from IPython.display import HTML, display, display_html

JSON is subset of AXON

Here is well known example of JSON message:


In [2]:
!cat basic_sample.json


{
  "firstName": "John",
  "lastName": "Smith",
  "age": 25,
  "address": {
     "streetAddress": "21 2nd Street",
     "city": "New York",
     "state": "NY",
     "postalCode": 10021
  },
  "phoneNumber": [
     { "type": "home",
       "number": "212 555-1234"
     },
     { "type": "fax",
       "number": "646 555-4567"
     }
  ]
}

In [3]:
json_vals= json.load(open("basic_sample.json"))
vals = axon.load("basic_sample.json", json=1)

One can see that content of json_vals and axon_vals are equal.


In [4]:
print(json_vals)
print(vals[0])
assert str(json_vals) == str(vals[0])


{'phoneNumber': [{'type': 'home', 'number': '212 555-1234'}, {'type': 'fax', 'number': '646 555-4567'}], 'firstName': 'John', 'age': 25, 'address': {'postalCode': 10021, 'streetAddress': '21 2nd Street', 'city': 'New York', 'state': 'NY'}, 'lastName': 'Smith'}
{'phoneNumber': [{'type': 'home', 'number': '212 555-1234'}, {'type': 'fax', 'number': '646 555-4567'}], 'firstName': 'John', 'age': 25, 'address': {'postalCode': 10021, 'streetAddress': '21 2nd Street', 'city': 'New York', 'state': 'NY'}, 'lastName': 'Smith'}

AXON supports more readable and compact form. Any JSON message can be translated to this form by removing of all , and " around keys that are identifiers:


In [5]:
axon.dumps(vals, pretty=1))


  File "<ipython-input-5-a43e32fdc007>", line 1
    axon.dumps(vals, pretty=1))
                              ^
SyntaxError: invalid syntax

We'l call these forms as JSON style of notation, which bases on compositions of dicts and lists.

For compatibility reasons there is a mode in pyaxon python library that allow using symbol ',' as a separator symbol between values in lists and dicts. In this case almost valid JSON message can be loaded as well as AXON message:

AXON as better JSON

AXON supports decimal numbers, date/time/datetime values and comments.

Let's consider AXON example – list of dicts containing decimal, date and time values:


In [6]:
!cat better_json.axon


{
  # first comment
  date: 2012-12-01
  time: 12:00
  debet: 1230$
  credit: 230$
}
{
  # second comment
  date: 2012-12-10
  time: 9:00
  debet: 2130$
  credit: 1020$
}
{
  # third comment
  date: 2012-12-20
  time: 15:00
  debet: 230$
  credit: 1200$
}


In [7]:
vals = axon.load("better_json.axon")
pprint(vals)


[{'credit': Decimal('230'),
  'date': datetime.date(2012, 12, 1),
  'debet': Decimal('1230'),
  'time': datetime.time(12, 0)},
 {'credit': Decimal('1020'),
  'date': datetime.date(2012, 12, 10),
  'debet': Decimal('2130'),
  'time': datetime.time(9, 0)},
 {'credit': Decimal('1200'),
  'date': datetime.date(2012, 12, 20),
  'debet': Decimal('230'),
  'time': datetime.time(15, 0)}]

There is compact dump:


In [8]:
print(axon.dumps(vals))


{credit:230D date:2012-12-01 debet:1230D time:12:00}
{credit:1020D date:2012-12-10 debet:2130D time:09:00}
{credit:1200D date:2012-12-20 debet:230D time:15:00}

There is a dump into formatted form:


In [9]:
print(axon.dumps(vals, pretty=1))


{ credit: 230D
  date: 2012-12-01
  debet: 1230D
  time: 12:00}
{ credit: 1020D
  date: 2012-12-10
  debet: 2130D
  time: 09:00}
{ credit: 1200D
  date: 2012-12-20
  debet: 230D
  time: 15:00}

There is also parameter hsize. It specifies maximum number of simple data items in a line:


In [10]:
print(axon.dumps(vals, pretty=1, hsize=2))


{ credit: 230D
  date: 2012-12-01
  debet: 1230D
  time: 12:00}
{ credit: 1020D
  date: 2012-12-10
  debet: 2130D
  time: 09:00}
{ credit: 1200D
  date: 2012-12-20
  debet: 230D
  time: 15:00}

AXON also supports reference links between values in the message. Here is an simple example:


In [11]:
!cat better_json_crossref.axon


# first leaf with label 1
&1 {
  id: 1
  value: "A"
}
# second leaf with label 2
&2 {
  id: 2
  value: "B"
}
# node with childrens
{
  id: 3
  children: [*1 *2]
}


In [12]:
vals = axon.load("better_json_crossref.axon")
assert vals[-1]['children'][0] is vals[0]
assert vals[-1]['children'][1] is vals[1]
pprint(vals)


[{'id': 1, 'value': 'A'},
 {'id': 2, 'value': 'B'},
 {'children': [{'id': 1, 'value': 'A'}, {'id': 2, 'value': 'B'}], 'id': 3}]

In [13]:
print(axon.dumps(vals, pretty=1, crossref=1, hsize=2))


&1 { id: 1
  value: "A"}
&2 { id: 2
  value: "B"}
{ children: [*1 *2]
  id: 3}

AXON extends JSON and makes things that XML can

Mapping as sort of named JSON object

Let's consider short fragment of JSON:

"name": {
    "key_1": "value_1", 
    "key_2": "value_2"
}

It can be translated as a value of the attribute name of some object. But it also can be translated as an object that is constructed from the value

{
    "key_1": "value_1", 
    "key_2": "value_2"
}

using some factory function that corresponds to the tag name.

For this kind of use cases there are mappings in AXON:

name {
    key_1: "value_1" 
    key_2: "value_2"
}

It's also usefull for notation of the object whose type/class is mapped to the name.

This kind of notation may be also considered as direct translation of the XML notation:

<name 
    key_1="value_1"
    key_2="value_2" />

Sequence as a sort of named JSON array

Let's consider another short fragment of JSON:

"name": [
    «value_1»,
    «value_2»
]

Some times this form is used for notation of the container of some type that corresponds to the tag name. For this kind of use cases there are sequences in AXON:

tag {
    «value_1»
    «value_2»
}

This kind of notation in AXON can be considered as translation of the following XML pattern:

<tag>
    «value_1»
    «value_2»
</tag>

AXON and XML

First basic example of JSON can be translated to AXON by following XML style of data representation when anonymous structures becames named and subelement is used instead of key:value or attribute:value value for some good reasons:


In [14]:
vals = axon.load("basic_sample.axon")
print(axon.dumps(vals, pretty=1))


person
  firstName: "John"
  lastName: "Smith"
  age: 25
  address
    streetAddress: "21 2nd Street"
    city: "New York"
    state: "NY"
    postalCode: 10021
  phoneNumber
    type: "home"
    number: "212 555-1234"
  phoneNumber
    type: "fax"
    number: "646 555-4567"

Here is it's XML version for comparison:

<person firstName="John" lastName="Smith" age="25">
    <address 
        streetAddress="21 2nd Street" 
        city="New York"
        state="NY"
        postalCode=10021 />
    <phoneNumber type="home" number="212 555-1234"/>
    <phoneNumber type="fax" number="646 555-4567"/>
</person>

By this way one can support extensibility of the representation as XML does when element is used instead of attribute. In case of XML this kind of notation is verbose for representation of name/value pair:

<attr>value</attr>

But in case of AXON it isn't:

attr{value}

So any XML element

<ename
  attr_1="value_m"
  ...
  attr_m="value_m">
  <subename_1>...</subename_1>
  ...
  <subename_N>...</subename_N>
</ename>

can be easily translated to AXON notation as follows:

ename {
  attr_1:"value_m"
  ...
  attr_m:"value_m"
  subename_1 {...}
  ...
  subename_N {...}
}

AXON formatting with/without braces

Presented above AXON forms of messages are are in compact form and in formatted with braces. Compact form uses minimum amount of space:


In [15]:
print(axon.dumps(vals))


person{firstName:"John" lastName:"Smith" age:25 address{streetAddress:"21 2nd Street" city:"New York" state:"NY" postalCode:10021} phoneNumber{type:"home" number:"212 555-1234"} phoneNumber{type:"fax" number:"646 555-4567"}}

Compact form of notation usually as small as many binary serialization formats in the cases when objects contains mostly strings and a few of numbers.

Formatted form is used for readability of expresson based format of representation:


In [16]:
print(axon.dumps(vals, pretty=1, braces=1))


person {
  firstName: "John"
  lastName: "Smith"
  age: 25
  address {
    streetAddress: "21 2nd Street"
    city: "New York"
    state: "NY"
    postalCode: 10021}
  phoneNumber {
    type: "home"
    number: "212 555-1234"}
  phoneNumber {
    type: "fax"
    number: "646 555-4567"}}

Note that one free to use spaces and line breaks between tokens as he want.

AXON also supports YAML/Python inspired formatted form without braces:


In [17]:
print(axon.dumps(vals, pretty=1))


person
  firstName: "John"
  lastName: "Smith"
  age: 25
  address
    streetAddress: "21 2nd Street"
    city: "New York"
    state: "NY"
    postalCode: 10021
  phoneNumber
    type: "home"
    number: "212 555-1234"
  phoneNumber
    type: "fax"
    number: "646 555-4567"

This type of notation is widely used in YAML. In AXON the same indentation level uses to indicate bounds of nested parts of the named block of data.

Mix all of them

Let's consider JSON text:

# formatted form
{"skillz": {
    "web": [
        {"name": "html", "years": "5", "level": "5"},
        {"name": "css", "years": "3", "level": "5"}
    ],
    "database": [
        {"name": "sql", "years": "7", "level": "4"}
    ]
}}
# compact form
{"skillz":{"web":[{"name":"html","years":"5","level":"5"},
{"name":"css","years":"3","level":"5"}],
{"name":"sql","years": "7", "level": "4"}]}}

and it's translation to XML-style representation in AXON formatted form (equal indentation of inner content of complex values is just usefull convention which is not required by syntax of AXON):

# formatted form
skills {
    web {
        html {years:5 level:5}
        css {years:3 level:5}}

    database {
        sql {years:7 level:4}}
}

# compact form
skills{web{html{years:5 level:5}} database{sql{years:7 level:4}}}

or in indented form (equal indentation of inner content of complex values is required by syntax of AXON):

skills
    web
        html
            years: 5
            level: 5
        css
            years: 3
            level: 5
    database
        sql
            years: 7
            level: 4


One could see that XML-style data representation has advantages over JSON-style in some context. The reverse is also the case.

Also one could see that formatted form of data representation has advantages over indented form in some context. The reverse is also the case.

More of that: you can mix them as you want in handwritten AXON representation:

skillz {
    web
        {name: "html"
         years: 5 
         level: 5}
        {name: "css"
         years: 3
         level: 5}
    database
        {name: "sql"
         years: 7
         level: 4}
}

or

skillz
    web
        {name: "html"
         years: 5 
         level: 5}
        {name: "css"
         years: 3
         level: 5}
    database
        {name: "sql"
         years: 7
         level: 4}

Reference implementation of AXON in python dosn't force you to choice one style or form of data representation over another. You can select them explicitly in dump call. But by default compact form of data representation is used.

It's hoped that existance in AXON of formatted expression based and indented statement based representations will help to combine these two ways of data representation when it's reasonable.

Example of formattings

For example, consider JSON-style representation of complex structure in formatted form:

{ store: {
    book: [ 
      { category: "reference"
        author: "Nigel Rees"
        title: "Sayings of the Century"
        price: 8.95
      }
      { category: "fiction"
        author: "Evelyn Waugh"
        title: "Sword of Honour"
        price: 12.99
      }
      { category: "fiction"
        author: "Herman Melville"
        title: "Moby Dick"
        isbn: "0-553-21311-3"
        price: 8.99
      }
      { category: "fiction"
        author: "J. R. R. Tolkien"
        title: "The Lord of the Rings"
        isbn: "0-395-19395-8"
        price: 22.99
      }
    ]
    bicycle: {
        color: "red"
        price: 19.95
    }
  }
}

There is XML-style representation of same structure in formatted form:

store {
    book { 
        category: "reference"
        author: "Nigel Rees"
        title: "Sayings of the Century"
        price: 8.95
    }
    book { 
        category: "fiction"
        author: "Evelyn Waugh"
        title: "Sword of Honour"
        price: 12.99
    }
    book { 
        category: "fiction"
        author: "Herman Melville"
        title: "Moby Dick"
        isbn: "0-553-21311-3"
        price: 8.99
    }
    book { 
        category: "fiction"
        author: "J. R. R. Tolkien"
        title: "The Lord of the Rings"
        isbn: "0-395-19395-8"
        price: 22.99
    }
    bicycle {
        color: "red"
        price: 19.95
    }
}

and in indented form:

store
    book
        category: "reference"
        author: "Nigel Rees"
        title: "Sayings of the Century"
        price: 8.95
    book
        category: "fiction"
        author: "Evelyn Waugh"
        title: "Sword of Honour"
        price: 12.99
    book
        category: "fiction"
        author: "Herman Melville"
        title: "Moby Dick"
        isbn: "0-553-21311-3"
        price: 8.99
    book
        category: "fiction"
        author: "J. R. R. Tolkien"
        title: "The Lord of the Rings"
        isbn: "0-395-19395-8"
        price: 22.99
    bicycle
        color: "red"
        price: 19.95

Last there is compact form:

store{book{category:"reference" author:"Nigel Rees" title:"Sayings of the Century" price:8.95}
book{category:"fiction" author:"Evelyn Waugh" title:"Sword of Honour" price:12.99}
book{category:"fiction" author:"Herman Melville" title:"Moby Dick" isbn:"0-553-21311-3" price:8.99}
book{category:"fiction" author:"J. R. R. Tolkien" title:"The Lord of the Rings" isbn:"0-395-19395-8" price:22.99}
bicycle:{color:"red" price:19.95}}

Named JSON objects

AXON extend JSON by allowing using of named objects by introducing nodes.

For example:

# formatted with braces
person {
    name: "John Hunt"
    age: 50
    weight: 87.5
}

# formatted without braces
person
    name: "John Hunt"
    age: 50
    weight: 87.5

Named JSON arrays

AXON also extend JSON by allowing using of named arrays. Note that JSON arrays are just lists in AXON.

For example:

# compact form
primes { 2 3 5 7 11 13 17 19 23 }

# mixed form
people {
    person
        name: "John" 
        age: 37
        sex: "male"
    person
        name: "Ann" 
        age: 27
        sex: "female"
    person
        name: "Jane" 
        age: 30
        sex: "female"
}

Hierarchical Objects

Hierarchical objects in AXON are objects, which contains sequence of child subobjects. They are represent attributed trees. So they can represent hierarchical data as well as XML can.

For example:

# compact form
tree{node{id:1 node{id:2 leaf{"Class A"}} node{id:3 leaf{"Class B"}}}}

# formatted with braces
tree {
   node {
      id:1
      node {
         id:2
         leaf { "Class A" }
      }
      node {
         id:3
         leaf { "Class B" }
      }
   }
}

# formatted without braces
tree
    node
      id:1
      node
         id:2
         leaf
            "Class A"
      node
         id:3
         leaf
            "Class B"

AXON for relational data

AXON supports using of object references for representation of relational data.

For example:

# formatted form with braces
graph {
    nodes: [
        # &1, &2, &3, &4 are labels of the values
        &1 node {1 1}
        &2 node {1 2}
        &3 node {2 2}
        &4 node {2 1}
    ]
    edges: [
        # *1, *2, *3, *4 are references of the values
        edge {*1 *2}
        edge {*1 *3}
        edge {*2 *3}
        edge {*1 *4}
        edge {*3 *4}
    ]
}

# formatted form without braces
graph:
    nodes: [
        # 1, 2, 3, 4 are labels of the values
        &1 node {1 1}
        &2 node {1 2}
        &3 node {2 2}
        &4 node {2 1}]
    edges:
        # *1, *2, *3, *4 are references of the values
        edge {*1 *2}
        edge {*1 *3}
        edge {*2 *3}
        edge {*1 *4}
        edge {*3 *4}]

In [ ]: