slonjs

Simple Lightweight Object Notation

Usage no npm install needed!

<script type="module">
  import slonjs from 'https://cdn.skypack.dev/slonjs';
</script>

README

slon

slon is Simple Lightweight Object Notation. Like json, but with less punctuation and some smart features.

slon:
// slon example

{
    name Shakespeare
    first_name William

    about """
        William Shakespeare was an English poet, playwright, and actor, 
        widely regarded as the greatest writer in the English language 
        and the world's greatest dramatist (Wikipedia)
    """
    
    books [
        {  title Hamlet    price 12.34    onSale true }
        {  title Macbeth   price 42.99    onSale false  }

        /* sold out
        { title "The Comedy of Errors" price 34.11 onSale no }
        */
    ]

    locations {
        Africa.Egypt.Alexandria Antony
        Europe.Greece.Athens Timon
        Europe.Italy.Venice  Othello
        Europe.Italy.Verona  Romeo
    }

    readers [Alice Bob Carol]
}
json:
{
    "name": "Shakespeare",
    "first_name": "William",
    "about": "William Shakespeare was an English poet, playwright, and actor,\nwidely regarded as the greatest writer in the English language\nand the world's greatest dramatist (Wikipedia)",
    "books": [
        {
            "title": "Hamlet",
            "price": 12.34,
            "onSale": true
        },
        {
            "title": "Macbeth",
            "price": 42.99,
            "onSale": false
        }
    ],
    "locations": {
        "Africa": {
            "Egypt": {
                "Alexandria": "Antony"
            }
        },
        "Europe": {
            "Greece": {
                "Athens": "Timon"
            },
            "Italy": {
                "Venice": "Othello",
                "Verona": "Romeo"
            }
        }
    },
    "readers": [
        "Alice",
        "Bob",
        "Carol"
    ]
}

layout

slon is a superset of json, that is, every valid json is also valid slon. Indentation is ignored, except within multiline strings. Array and object delimiters (commas/colons) are optional, object keys don't have to be quoted.

slon supports line comments with # or // and C-style comment blocks /* ... */

slon:
# sample data

/*
    @author: me
    @date: today
*/

{
    name Shakespeare // author's name
    year 1564        // year of birth

    books [Hamlet Macbeth Othello]

    Alice: true,
    Bob: true,
    Carol: true,
}
json:
{
    "name": "Shakespeare",
    "year": 1564,
    "books": [
        "Hamlet",
        "Macbeth",
        "Othello"
    ],
    "Alice": true,
    "Bob": true,
    "Carol": true
}

values

Out of the box, slon can encode nulls, numbers, booleans, strings, arrays and objects.

null

A null value is null or none, case-insensitive:

slon:
[ null NULL None none ]
json:
[
    null,
    null,
    null,
    null
]

numbers

slon supports decimal, hexadecimal, octal and binary integers and decimal floats in the standard notation. Unlike json, leading zeroes are allowed. A number can be preceded by a - or +. Underscores can be used to delimit long numbers.

slon:
[
    123
    123_456_789
    +045.990
    -12.3e4
    0xcafe
    0o755
    0b1110011
]
json:
[
    123,
    123456789,
    45.99,
    -123000.0,
    51966,
    493,
    115
]

booleans

Boolean literals are true, on, yes for "true", false, off, no for "false", case-insensitive.

slon:
{
    select true
    update YES
    insert ON
    delete no
    create Off
}
json:
{
    "select": true,
    "update": true,
    "insert": true,
    "delete": false,
    "create": false
}

strings

plain strings

Plain strings are enclosed in single or double quotes and cannot contain a literal newline.

Single quoted strings are verbatim, that is, every symbol is interpreted exactly as written.

slon:
'Hamlet \xa9 William, 1599. \n bravo \U0001F44F \u{1f44f}'
json:
"Hamlet \\xa9 William, 1599. \\n bravo \\U0001F44F \\u{1f44f}"

Double quoted strings interpret these escape sequences:

\n  \r  \t  \b  \f  \\  \"  \'  \/  \\  \0

and unicode escapes in these formats:

\x12  \u1234  \u{12345}  \U00012345
slon:
"Hamlet \xa9 William, 1599. \n bravo \U0001F44F \u{1f44f}"
json:
"Hamlet © William, 1599. \n bravo 👏 👏"

slon decodes valid unicode surrogate pairs into plane 1 characters:

slon:
"smile \ud83d\ude03"
json:
"smile 😃"

slon doesn't allow unicode escapes greater than \u{10FFFF}, but otherwise makes no attempt to validate unicode. Particularly, invalid surrogate pairs and non-characters are left as is.

multiline strings

Strings enclosed in triple quotes (''' or """) or backticks can span across multiple lines.

If a character right after opening quotes is a space or a newline, newlines in the string are preserved, and the string is dedented to its minimal indent:

slon:
"""
            Scene I.        

        BERNARDO
            Who's there?
        FRANCISCO
            Nay, answer me: stand, and unfold yourself.
"""
json:
"    Scene I.\n\nBERNARDO\n    Who's there?\nFRANCISCO\n    Nay, answer me: stand, and unfold yourself."

Otherwise, the string is "compressed", so that all whitespace sequences are reduced to a single space character:

slon:
"""You are welcome, masters;
welcome, all.

I am glad
to see thee well.
"""
json:
"You are welcome, masters; welcome, all. I am glad to see thee well."

''' strings are verbatim, """ and backtick strings interpret the escape sequences. Note that escaped spaces or newlines are preserved when the string is dedented or compressed:

slon:
`Enter a King\n
and a Queen very lovingly;\n
the Queen embracing him,\n
and he her.`
json:
"Enter a King\n and a Queen very lovingly;\n the Queen embracing him,\n and he her."

bare strings

Any sequence of symbols except whitespaces, punctuation and numeric/bool literals is considered a string, so you can write "simple" strings without any quotes at all:

slon:
[
    Hamlet
    claudius@elsinore.castle
    $123.45
]
json:
[
    "Hamlet",
    "claudius@elsinore.castle",
    "$123.45"
]

arrays

An array (aka list) is a sequence of values separated by commas or whitespaces, enclosed in [...]:

slon:
[
    Shakespeare
    William
    "The Tragedy of Hamlet" 
    [
        2017,
        2018,
        2019,
    ]
]
json:
[
    "Shakespeare",
    "William",
    "The Tragedy of Hamlet",
    [
        2017,
        2018,
        2019
    ]
]

objects

An object (aka struct, dict) is a sequence of key-value pairs, separated by commas or whitespaces and enclosed in {...}. Keys and values are separated by colons or equal signs or whitespaces. Keys can be strings, integers or booleans:

slon:
{
    author
        Shakespeare
    year
        1599
    "full title": 'The Tragedy of Hamlet'
    seasons {
        2017 = yes
        2018 = no
        2019 = yes
    } 
}
json:
{
    "author": "Shakespeare",
    "year": 1599,
    "full title": "The Tragedy of Hamlet",
    "seasons": {
        "2017": true,
        "2018": false,
        "2019": true
    }
}

advanced usage

structured keys

An object key which is a non-quoted ("bare") string can contain dots, in which case slon treats it as a structured ("nested") key and resolves intermediate objects or arrays, creating them on the fly if needed:

slon:
{
    title.short  Hamlet
    
    price { normal 12.34 }

    price.sale
        5.67
    price.special.christmas   
        8.99

    readers.0.name Alice
    readers.1.name Bob
    readers.2.name Carol
}
json:
{
    "title": {
        "short": "Hamlet"
    },
    "price": {
        "normal": 12.34,
        "sale": 5.67,
        "special": {
            "christmas": 8.99
        }
    },
    "readers": [
        {
            "name": "Alice"
        },
        {
            "name": "Bob"
        },
        {
            "name": "Carol"
        }
    ]
}

A + after a key means "append to that array":

slon:
{
    author Shakespeare
    title Hamlet

    readers+ { name Alice }
    readers+ { name Bob }
    readers+ { name Carol }
}
json:
{
    "author": "Shakespeare",
    "title": "Hamlet",
    "readers": [
        {
            "name": "Alice"
        },
        {
            "name": "Bob"
        },
        {
            "name": "Carol"
        }
    ]
}

If you need a literal dot in a key, quote it:

slon:
{
    "com.sun.java" installed
}
json:
{
    "com.sun.java": "installed"
}

hooks

A "bare" string immediately followed by an opening parenthesis ( is treated as a function name ("hook"). A hook function receives one of the basic values and can return whatever you see fit. slon parser accepts the hooks argument, which is an object or a dict containing hook functions (or methods).

You can use hooks to add support for library types like dates or binary strings. For example, this slon file:

// test.slon

{
    title
        Hamlet
    created
        date('1599-02-20')
    image
        bin("\x89PNG\r\n\x1a\n\0\0\0...")
}

can be decoded with this code:

from datetime import date
from functools import partial

hooks = {
    'date': date.fromisoformat,
    'bin': partial(bytes, encoding='latin1'),
}

with open('test.slon') as fp:
    result = slon.loads(fp.read(), hooks=hooks)

print(result) ## {'title': 'Hamlet', 'created': datetime.date(1599, 2, 20), 'image': b'\x89PNG\r\n\x1a\n\x00\x00\x00...'}

APIs

python

pip install slon

The slon module provides a single function:

slon.loads(text: str, as_object=False, as_array=False, hooks=None) -> Any
  • text is a string (str) which contains encoded slon. Note that bytes inputs are not supported

  • as_object parses text as an object, that is, puts implicit {...} around it, unless text already starts with a {

  • as_array parses text as an array

  • hooks is a hooks dict or object

Examples:

text = """
    author Shakespeare
    title Hamlet
"""

val = slon.loads(text, as_object=True)

print(val)  # {'author': 'Shakespeare', 'title': 'Hamlet'}


text = """
    100
    200
    300
"""

val = slon.loads(text, as_array=True)

print(val)  # [100, 200, 300]

javascript

npm install slonjs

The slon module provides a single function:

slon.parse(text: string, options: object) -> any

Options are asObject, asArray and hooks, with the same meaning as in python.

let slon = require('slon');

text = `
    author Shakespeare
    title Hamlet
`

val = slon.parse(text, {asObject: true})

console.log(val)  // {author: 'Shakespeare', title: 'Hamlet'}

For browsers, grab index.js and use window.SLON.parse().

info

(c) 2019 Georg Barikin (https://github.com/gebrkn). MIT license.