edn: extensible data notation

In this article I want to talk about edn. edn — format data generated from clojure. It is similar to JSON, but provides some features not present in the JSON. Edn features described below. An example for priming:

the
{:name "edn"
:implementations #{"clojure" "java" "ruby" "python" "c" "javascript" "haskell" "erlang"}
:related "clojure"
:encoding :UTF-8}


Appearance

The history of the emergence edn similar to the appearance JSON: first a programming language, and then he singled out a subset and were used as data format. If JSON the language of the progenitor of JavaScript, for edn is Clojure.

As I said earlier edn and JSON are very similar and considering that JSON is now the most known, simple and popular data format, I'm going to talk about the edn through its differences from JSON.

Edn supports all simple types that are present in JSON: strings, numbers, Boolean values. As there are new:

nil

As null values in the edn use the value nil. JSON uses null.

signs (characters)

edn supports signs to indicate separate characters. They start with a backslash: \e, \d, \n. You can use the UTF8 format: \u2603. Special characters are written in full: \newline, \return, \space, \tab.
In JSON the individual characters are usually represented as strings of length 1.
I don't call characters characters, because edn is a separate type of symbol, which will be described below.

keyword symbols (keywords)

I find it difficult to articulate what the key symbols. I would say that it is a mixture of strings with enums. It is convenient to use when there is a finite fixed set of possible values. These values can be set keywords. Also accepted as keys of maps to use key symbols. Key symbols start with a colon: :name, :lastname, :female, green. Those who worked with ruby needs to know in these characters, like the types present in other languages, e.g. common lisp.

An example of using keywords in the display and compared with the JSON version

the the
edn JSON
{:name "Jack"
:lastname "Brown"
:gender :male}

{"name": "Jack",
"lastname": "Brown",
"gender": "male"}



number

edn parts 2 kinds of numbers: integer and real. It also supports arbitrary long numbers using the suffix N for integers and M material:

the
[12345678891231231231232133N, 123.123123123123123213213 M]


vector

JSON'ovsky edn array is called a vector: a sequence of values for which the operation support random access. Unlike JSON elements should not be separated by commas. You can omit them:

the
[1 2 3 "Hello" "World"]


display (maps)

In JSON they are called objects. In the edn the key values are separated by colons (colon — error). Commas can also be omitted. As keys can be any other type, such as a number or the keywords:

the
{:name "Jack"
:lastname "Brown"
:gender :male
42 54}


Here is a map with keys name, :lastname, :gender and 42, values "Jack", "Brown", :male, 54.

sets

edn supports the data type set. It is specified in the format #{val1 val2 val3}. Order in not important and many parsers do not guarantee any specific order. In principle, the parser needs to convert standard programming language type, such as HashSet for java, PersistentHashSet to clojure and similarly for other languages. And in these types of data order no and cannot be guaranteed. Example: we define a very useful display containing seasons and 3 colors:

the
{:seasons #{:winter :spring :summer :autumn}
:colors #{[255 0 0] [0 255 0] [0 0 255]}}


lists

edn in addition to the vector supports lists. This list differs from vector is that there is no random access. Although it is a matter of the parser to some real type it converts the list. It is difficult to figure out when it may be more convenient to use list instead of vector. So the vector in the majority of cases and used. Example list:
the
(1 2 3 4 5)


symbols (symbols)

In clojure, symbols are used for variables. I.e. similar to the identifiers in common programming languages: a, b, i, hello, persons. The character of several words divided by a hyphen: prime-numbers, visited-nodes. They can contain also numbers and letters the following characters: . * + ! - _ ? $ % & =. It's hard to think of a way to use simvolov in the edn when there are rows and key characters. It depends on your imagination. In datomic they are used to send requests, for example:

the
[:find ?x :where [?x :foo]] 
?x — the symbol.

elements with tags (tagged elements)

edn supports the possibility of extension through tags. Tag — the identifier that starts with #, followed by the symbol: #inst, #uuid, #myapp/Person. The peculiarity of these elements is that the parser, when it encounters this element reads it and any following element, passes to a special handler that needs to transform an input Argumenty to the desired type and return it. Examples:

the
#myapp/Person {:first "Fred" :last "Mertz"}

Then in the parser must be registered with the handler #myapp/Person, which takes the display and converts it into an object of class myapp.Person (if the language has classes), or something like that.

the
#inst "1985-04-12T23:20:50.52 Z"

The handler takes a string in the format RFC 3339 and converts to the appropriate date.

the
#uuid "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"

The handler converts the UUID string to the corresponding object.

The last 2 tags are built-in and should work out of the box. There is a limit that custom tags should always have a namespace in the beginning as in the case of #myapp/Person: here myapp — namespace. Tags without a namespace (e.g. #inst, #uuid) are reserved for standard handlers.

comments

For comments use ;. With the help of it you can thecommunity line:
the
{
:red [255 0 0] ; Red 255, green 0, blue 0
:orange [255 127 0] ; Red 255, green 127, blue 0
}

More complete example edn

Here is an example of the list of all users that visited the site for the last couple of days. Example far-fetched, and the purpose of it is to demonstrate that edn is again:

the
[{:name "Jack"
:lastname "Brown"
:roles #{:admin :operator}
:last-visited #inst "2013-04-12T23:20:50.52 Z"
:id #uuid "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"}
{:name "John"
:lastname "Black"
:roles #{:user}
:last-visited #inst "2013-04-12T20:20:50.52 Z"
:id #uuid "b371b600-b175-11e2-9e96-0800200c9a66"}]


When to use

This is a subjective question. All the above features can be implemented in JSON introducing its own designs, but it requires more complex logic for converting to/from JSON. In edn they are out of the box, which is very convenient. If you are working with clojure, edn is a natural choice. Maybe you are tired of boring JSON and would like to work with more flexible and customizable format that can help tags. The presence of "standard" type for the date is also a nice feature. We can say that the edn — JSON on steroids.

Links

The official description of the format: github.com/edn-format/edn
Implementations for java, ruby, python, javascript, haskell and other languages: github.com/edn-format/edn/wiki/Implementations
The discussion on Hacker News: news.ycombinator.com/item?id=4487462 There is just arguing about "Why the edn, if you have JSON?"
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Briefly on how to make your Qt geoservice plugin

Database replication PostgreSQL-based SymmetricDS

Yandex.Widget + adjustIFrameHeight + MooTools