Skip to content

whyolet/jonf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JONF icon

Json for cONF

# Fictional supercomputer IaC

name - Deep Thought
answer - 42

hardware =
  cores = 42
  eyes =
    left - green
    right - violet

about -
  indented, unquoted,
  and raw - \no special chars

  multiline string here

pets =
  - cat
  - dog
  - turtle  # or tortoise

friends =
  =
    name - Alice
    age = null
  =
    name - Bob
    age = 42

scripts =
  check -
    set -eu  # No more && chains
    DIRS="src tests"
    lint $DIRS
    test $DIRS

Equal JSON:

{
  "name": "Deep Thought",
  "answer": "42",
  "hardware": {
    "cores": 42,
    "eyes": {
      "left": "green",
      "right": "violet"
    }
  },
  "about": "indented, unquoted,\nand raw - \\no special chars\n\nmultiline string here",
  "pets": [
    "cat",
    "dog",
    "turtle"
  ],
  "friends": [
    {
      "name": "Alice",
      "age": null
    },
    {
      "name": "Bob",
      "age": 42
    }
  ],
  "scripts": {
    "check": "set -eu\nDIRS=\"src tests\"\nlint $DIRS\ntest $DIRS"
  }
}
  • Make JONF more readable and suitable for configuration files and DSLs than JSON
  • Keep JONF simple and flawless, unlike YAML
  • Do not sacrifice JSON features to achieve previous goals, unlike StrictYAML and NestedText

JSON is great for API, but not as great for config files and DSLs, that's why alternatives are trying to fill the gap:

  1. ELDF
  2. ENO
  3. HCL
  4. HJSON
  5. HOCON
  6. HRON
  7. ION
  8. JSON5
  9. Jsonnet
  10. NestedText
  11. SDLang
  12. StrictYAML
  13. TOML
  14. YAML
    xkcd: Standards
  15. JONF
  • Disclaimer: I excluded XML from the competition to match this XKCD image, also XML hardly competes for goal 1 anyway
  • Competition is not a bad thing at all - this is how everything evolves, and it shows there is a market for the product
  • Most alternatives above, especially the most popular YAML, are much more complex than JSON and/or have other flaws - please see the great analysis by StrictYAML
  • The Norway Problem is a trap even so popular YAML falls into
  • StrictYAML and NestedText solve this problem by assuming all scalars to be strings, and then using optional external schema to convert these strings to the intended type
  • This means standard representation of numbers, true, false, and null is no longer supported without external schema
  • Going this route we may start using just plain text format because the app should validate it anyway
  • While JSON does not have standard representation of dates, times, and so on, to keep it unbloated, existing JSON scalar types are heavily used, so we'd better try to keep them
  • JONF solves The Norway Problem and keeps JSON scalar types in an elegant and terse way

Key-value separators and array markers:

  • - is for unquoted strings
  • = is for all other values

Bash memo:

  • - is like single quote '
  • = is like double quote "
  • - leads to unquoted string on the same line
    • or to indented unquoted multiline string on the next lines
  • = leads to double-quoted JSON string or another JSON value on the same line
    • or to indented JONF object on the next lines
    • or to indented JONF array on the next lines
  • It is possible to introduce values of custom types after =, e.g. numerous ISO 8601 dates and times formats, but this is a way either to growth of complexity of standard and each parser, or to interoperability hell with user-provided callbacks to parse custom types, so this feature should not be implemented
  • DSL, e.g. serverless variables, should be parsed after JONF is parsed, see example 10. DSL
  • You can have any indentation style that you want so long as it is black "two spaces", because standardized indentation improves readability and helps to avoid bugs
  • For the same reason, exactly one space char is required:
    • after array marker
    • before key-value separator
    • after key-value separator if the value is on the same line

One-line non-indented JSON value is a valid JONF root value, because in a one-line case, JSON is great:

{"some": "object", "key": "value"}

["some", "array", "here"]

"some string"

-3.14

true

false

null

JONF object, JONF array, JONF indented unquoted multiline string - are valid root values too, e.g:

  indented unquoted
  multiline

  string

Equal JSON:

"indented unquoted\nmultiline\n\nstring"

More features of unquoted string are explained in the next examples

- Alice in Wonderland
-
  multiline
  string

  here

= "multiline\nstring\n\nhere"
= "  explici\t whitespace \n"
- unquoted is raw - \no special chars"
- great for regex: [\n\r\t]+
- 42
= 42
- -3.14
= -3.14
- true
= true
- false
= false
- null
= null
- []
= []
- {}
= {}
- ""
= ""

Equal JSON:

[
  "Alice in Wonderland",
  "multiline\nstring\n\nhere",
  "multiline\nstring\n\nhere",
  "  explici\t whitespace \n",
  "unquoted is raw - \\no special chars\"",
  "great for regex: [\\n\\r\\t]+",
  "42",
  42,
  "-3.14",
  -3.14,
  "true",
  true,
  "false",
  false,
  "null",
  null,
  "[]",
  [],
  "{}",
  {},
  "\"\"",
  ""
]
name - Deep Thought
answer - 42
cores = 42
"some - strange = key" - value
42 - keys are always strings
true = "even with =, it affects values only"

Equal JSON:

{
  "name": "Deep Thought",
  "answer": "42",
  "cores": 42,
  "some - strange = key": "value",
  "42": "keys are always strings",
  "true": "even with =, it affects values only"
}
type - dragon
eyes =
  left - green
  right - violet

Equal JSON:

{
  "type": "dragon",
  "eyes": {
    "left": "green",
    "right": "violet"
  }
}
=
  name - Alice
  age = null
=
  name - Bob
  age = 42

Equal JSON:

[
  {
    "name": "Alice",
    "age": null
  },
  {
    "name": "Bob",
    "age": 42
  }
]

Please note = should be used as an array marker, unless you want unquoted strings:

- unquoted string
-
  multiline - unquoted
  string = here

Equal JSON:

[
  "unquoted string",
  "multiline - unquoted\nstring = here"
]

JONF is arguably more readable than YAML below, let alone YAML's typing flaw:

person:
  name: abcd
  nick: efgh
friends:
- name: hijk
  nick: lmno
- name: pqrs
  nick: tuvw

It looks like all names here are on the same depth in YAML, which is misleading, and it takes some effort to notice the boundaries of each friend

JONF shows the depth and boundaries correctly, improving readability:

person =
  name - abcd
  nick - efgh
friends =
  =
    name - hijk
    nick - lmno
  =
    name - pqrs
    nick - tuvw
name - Bob
kids =
  - Charlie
  - Dave
  - Eve

Equal JSON:

{
  "name": "Bob",
  "kids": [
    "Charlie",
    "Dave",
    "Eve"
  ]
}
=
  - We
  - are
=
  - almost
  =
    - done!

Equal JSON:

[
  [
    "We",
    "are"
  ], [
    "almost",
    [
      "done!"
    ]
  ]
]

JONF is obviously better than JSON in a multi-line case

Again, JSON is the best for one-liners, that's why they are valid in JONF:

[["We", "are"], ["almost", ["done!"]]]

Text from # to the end of line is a comment if # is the first character in the line or there is a whitespace before it and it is not inside of JSON value:

# Full-line comment
name - Alice  # Inline comment
url - https://example.org/#alice
location = "Wonderland # 42"

Equal JSON:

{
  "name": "Alice",
  "url": "https://example.org/#alice",
  "location": "Wonderland # 42",
}

JONF uses # and never // or /* */ for the same reason JSON uses double-quotes and never single-quotes: to keep it simple

In serverless.jonf variables are parsed after JONF is parsed:

custom =
  debug = true
  verbose - ${self:custom.debug}

Equal JSON:

{
  "custom": {
    "debug": true,
    "verbose": "${self:custom.debug}"
  }
}

Equal JSON, parsed by DSL:

{
  "custom": {
    "debug": true,
    "verbose": true
  }
}

Note that DSL decided to change type of verbose to the type of the variable it referenced. JONF never does that, it is just a container format, strictly mapped to JSON only

  • Add JONF to github/linguist and Rouge highlighters to simplify the reviews
  • Reviews and fixes of this draft specification
  • Formal grammar importing JSON grammar but excluding newlines from its ws rule
  • Apply this grammar to the reference JONF parser/formatter in Python
  • Bump to version 1.0.0
  • Get contributed JONF parsers/formatters for other languages
Name - JONF
Version - 0.0.17
Filename extension - .jonf
Internet media type - application/jonf  # TODO
Website - jonf.app
Email - support@jonf.app
Maintainer - whyolet.com
License - MIT
Open format? - Yes
Type of format - Data interchange
Extended from - JSON

Simple and valuable, right?

Thank you for reading

Please post an issue or idea or contribute otherwise:

GitHub icon https://github.com/whyolet/jonf