📜 ⬆️ ⬇️

edn: extensible data notation

In this article I want to tell about edn. edn is a data format derived from the clojure language. It is similar to JSON, but provides some features not found in JSON. The features of edn are described below. Example for priming:

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


Appearance

The history of the appearance of edn is similar to the appearance of JSON: a programming language first appeared, and then a subset was selected from it and began to be used as a data format. If for JSON the progenitor language is JavaScript, then for edn is Clojure.

As I said earlier, edn and JSON are very similar, and considering that JSON is now the most famous, simple and popular data format, I will talk about edn through its differences from JSON.
')
In edn, all simple types that are present in JSON are supported: strings, numbers, booleans. There are also new ones:

nil

The zero value in edn is nil . JSON uses null .

characters

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

key characters (keywords)

I find it difficult to formulate what are the key characters. I would say that this is a mixture of strings with enumerations. It is convenient to use them when there is a finite fixed set of possible values. These values ​​can be set by keywords. It is also customary to use key symbols as keys of mappings. Key characters begin with a colon:: :name :lastname :female :lastname :green . Those who worked with Ruby should recognize symbols in them, similar types are present in other languages, for example common lisp.

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

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

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



numbers

edn separates 2 types of numbers: integers and real. It also supports numbers of arbitrary length using the suffix N for integers and M for real ones:

 [12345678891231231231232133N, 123.123123123123123213213M] 


of vector

A JSON array in edn is called a vector: a sequence of values ​​for which a random access operation is supported. Unlike JSON, elements should not be separated by commas. They can be omitted:

 [1 2 3 "Hello" "World"] 


mappings

In JSON, they are called objects. In edn, the key is not separated from the value by a colon (the colon is an error). Commas can also be omitted. The keys can be any other type, for example, numbers or keywords:

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


Here is the mapping with the keys :name :lastname :gender and 42 , the values ​​are respectively "Jack" , "Brown" :lastname :male , 54 .

sets

edn supports the data type set. It is given in the format #{val1 val2 val3} . The order in the set is not important and the parsers do not guarantee any particular order. In principle, parsers should convert to a standard for a programming language type, for example, HashSet for java, PersistentHashSet for clojure, and similarly for other languages. And in these data types, no order is guaranteed. Example: let's set a very useful display containing the seasons and 3 colors:

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


lists

edn except vector supports lists. The list differs from the vector in that there is no random access. Although this is already a matter of the parser into which real type it converts the list. It is generally difficult to think of when it may be more convenient to use a list, and not a vector. So the vector is overwhelmingly used. Example list:

 (1 2 3 4 5) 


symbols

In clojure, symbols are used to refer to variables. Those. similar to identifiers in common programming languages: a , b , i , hello , persons . The symbol of several words can be separated by a hyphen: prime-numbers , visited-nodes . In addition to numbers and letters, they may contain the following characters:. . * + ! - _ ? $ % & = . * + ! - _ ? $ % & = . It's hard for me to think of a way to use symbols in edn, when there are lines and key characters. It already depends on your imagination. In datomic, they are used to send requests, for example:

 [:find ?x :where [?x :foo]] 
?x is a symbol.

tagged elements

edn supports the possibility of extensions using tags. Tag is an identifier that begins with # , followed by the symbol: #inst , #uuid , #myapp/Person . The peculiarity of such elements is that the parser, when it encounters such an element, reads it and the element following it, passes it to a special handler, which must convert the input arguments to the desired type and return. Examples:

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

Here the handler of the tag #myapp/Person must be registered in the parser, which accepts the mapping and converts it into an object of the class myapp.Person (if there are classes in the language), or something similar.

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

The handler accepts a string in RFC-3339 format and converts it to the appropriate date.

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

The handler converts the UUID string to the corresponding object.

The last 2 tags are inline and must work out of the box. There is also a limitation that custom tags should always have a namespace at the beginning, as in the case of #myapp/Person : here myapp is the namespace. Tags without namespace (for example #inst , #uuid ) are reserved for standard handlers.

comments

Used for comments ; . With it you can comment out the line:
 { :red [255 0 0] ; Red 255, green 0, blue 0 :orange [255 127 0] ; Red 255, green 127, blue 0 } 

A more complete example edn

Here is an example of a list of all users who visited the resource over the past couple of days. An example is contrived and its purpose is to demonstrate edn again:

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


When to use

This is a subjective question. All the above features can be implemented in JSON by introducing your own constructs, but this requires more complex logic to convert to / from JSON. In edn, they are out of the box, which is very convenient. If you are working with clojure, then edn is the natural choice. You may also be tired of boring JSON and would like to work with a more flexible and customizable format, in which the tags can help. The presence of a “standard” type for a date is also a nice feature. You could say that edn is JSON on steroids.

Links

The official format description: github.com/edn-format/edn
Implementations for java, ruby, python, javascript, haskell and other languages: github.com/edn-format/edn/wiki/Implementations
Hacker News Talk: news.ycombinator.com/item?id=4487462 They just swear about "Why edn, if there is JSON?"

Source: https://habr.com/ru/post/178473/


All Articles