Format

Anyone can write his own odn library, but you must respect all the things listed below.

Introduction

Odin is able to generate 2 types of outputs, indented or compressed:

  • Indented


    A format can be used for configuration files or data storage that need to be readable by humans.
    Each entry is separated with a system line separator ( CR (old Mac OS), CRLF (Windows) or LF (Linux, Mac OSX) ) and each depth adds a tabulation before the entry starts.
    Strings containing line separators are also written as multi lines strings where the line separators are written without the escape character.
    Since indented outputs allow the use of comments and blank lines, these special lines can be read and will be kept during the rewriting.

  • Compressed


    The default format, used for network or data storage.
    A comma is used as entry separator and no more space or tabulation is added, all line separators in the Strings are escaped.

Entries

There are 3 types of entries, each provides a specific use case:

  • Value


    Used to write objects or primitives directly in the output as a list.
    For example you can write an integer, then a boolean and a Foo object.
    When reading you have to reuse the same order and read an integer, a boolean and the object from the input.

  • Field


    Used to name a value, a field has a String as name and can contain any UTF-8 characters except odn delimiters: =:<>{} [],'" .
    The key also adds \ before a special character that needs to be escaped: \\r\n\t\f\b .
    The field value is like a simple value, which can be anything. The key and the value of the field are separated by a = character.

    key = "value"
  • Row


    Row is something a little bit more specific. It's an entry that can contain an unlimited number of values, like a spreadsheet row.
    Each value is separated by a : character.

    13 : 21 : 34

Contents

All primitives can be written and get these given forms:

  • Number and Boolean


    No more characters are written, these primitives are directly written in the output.

    a boolean = true
    a number  = 15
  • Character


    A character is written between ' characters.
    A \ is added before the character if it needs to be escaped, basically '\\r\n\t\f\b.

    a char = '\\'
  • String


    A String is written between " characters and like character a \ is added before special characters: "\\t\f\b.
    In case of indented output \n and \r are not escaped and a multiline String is written. If the new line starts with a space or a tabulation, a \ is added before, in order to known where the line starts.

    a string = "
        A multiline String
        \ with a space after a new line
    "
  • Null


    A special key null is directly written in the output.

    an object = null

Array is written using [], the whole content is written between its brackets.


Other objects use {}.



Arrays and objects also use the type definition. If the object to write is not of the same type as the generic type, like an abstract class, the class definition is written before the object and is put between <> characters. If Odin contains a type definition for this class, the alias is written, else the class name is used.

<foo> {
    ...
}

In fact, primitives can also be written after a type definition in case of an object using an inline adapter with a primitive result.

<local date> "2018-02-25"

Comments

As explained above, the indented format can contain comments. A comment always starts by a # and only a new line closes the comment.


A comment cannot be placed after a value in the same line.

# a comment line that can be used to describe the next entry
lines = 5

Hierarchical references

Objects have a recursive reference security, if an object writes a field and this field is a reference to the parent, Odin writes a reference to the parent instead of infinitely looping on it. The reference is written between () and is the depth between the current object and the referenced object in the ascendant hierarchy. 0 is the current object ( an object referencing itself ), 1 is for the direct parent, and so on.

{
    id = 5
    content = {
        parent = (1)
    }
}

Technical note

When reading odn, all useless white spaces are skipped, considering that the keys are trimmed and only the white spaces inside the characters and String are kept. If you need to include white spaces at the start or the end of a key, you can add a \ before the white space. Thanks to this, the trim does not affect the white spaces between the escaped and other characters.



Writing and reading are done in a non-blocking way, you can read an object, do a process and later read the next object. By this way, the odn format has a very small memory footprint, each read object is directly converted in its Java object representation.

{...},{...}
odin.read();
// do some actions
odin.read();