7. Maps
Readable Object Notation

A map, called an object in JSON, contains string keys paired with values. Any value may appear after a key, including null, another map, or a list. An empty map is written {}.

Compact maps alternate keys and values, separated by whitespace. Each ordinary member has both a key and a value. A tag group supplies boolean members using the shorthand described below. There is no colon between a key and its value.

Canonical nonempty nested maps put the first key/value pair immediately after { and align each following pair beneath it. Each pair occupies its own line, including in maps with one member. A comment that must precede the first item puts the opening brace on its own line. The empty map remains {}:

RON
single {x 1}
multiple {x 1 y 2}
empty {}
→
Canonical RON
empty {}
multiple {x 1
          y 2}
single {x 1}
→
{} JSON
{
  "single": {
    "x": 1
  },
  "multiple": {
    "x": 1,
    "y": 2
  },
  "empty": {}
}
RON
{name Ada age 36 active true}
→
Canonical RON
<active>
age 36
name Ada
→
{} JSON
{
  "name": "Ada",
  "age": 36,
  "active": true
}

A map with { alone on its opening line has one ordinary member per line and may also contain tag groups. Each ordinary member begins with its key, followed by its value. The first member may follow the opening brace on the same line, and the last value may be followed immediately by the closing brace.

RON
{
  name Ada Lovelace
 age 36
 active true}
→
Canonical RON
<active>
age 36
name Ada Lovelace
→
{} JSON
{
  "name": "Ada Lovelace",
  "age": 36,
  "active": true
}

When the first member follows { on the opening line, the map is packed: each line contains whitespace-separated key/value pairs and optional tag groups. A packed map can wrap between complete pairs. Values containing spaces need quotes.

RON
settings {
  <active !admin>
  field00 value00
  field01 value01
  field02 value02
  field03 value03
  field04 value04
  field05 value05
  field06 value06
  field07 value07
  field08 value08
  field09 value09
  field10 value10
  field11 value11
  field12 value12
  field13 value13
  field14 value14
  field15 value15
  field16 value16
  field17 value17
  field18 value18
  field19 value19
}
→
Canonical RON
settings {<active !admin>
          field00 value00
          field01 value01
          field02 value02
          field03 value03
          field04 value04
          field05 value05
          field06 value06
          field07 value07
          field08 value08
          field09 value09
          field10 value10
          field11 value11
          field12 value12
          field13 value13
          field14 value14
          field15 value15
          field16 value16
          field17 value17
          field18 value18
          field19 value19}
→
{} JSON
{
  "settings": {
    "active": true,
    "admin": false,
    "field00": "value00",
    "field01": "value01",
    "field02": "value02",
    "field03": "value03",
    "field04": "value04",
    "field05": "value05",
    "field06": "value06",
    "field07": "value07",
    "field08": "value08",
    "field09": "value09",
    "field10": "value10",
    "field11": "value11",
    "field12": "value12",
    "field13": "value13",
    "field14": "value14",
    "field15": "value15",
    "field16": "value16",
    "field17": "value17",
    "field18": "value18",
    "field19": "value19"
  }
}

A tag group also counts as content on the opening line. Its position selects how the ordinary member lines are read. With { alone on the opening line, the whole bare value Ada Lovelace PhD belongs to name:

RON
settings {
  <active>
  name Ada Lovelace PhD
}
→
Canonical RON
settings {<active>
          name "Ada Lovelace PhD"}
→
{} JSON
{
  "settings": {
    "active": true,
    "name": "Ada Lovelace PhD"
  }
}

Moving the tag group after { selects packed mode. The same following line now contains two pairs: name is Ada, and Lovelace is PhD:

RON
settings {<active>
  name Ada Lovelace PhD
}
→
Canonical RON
settings {<active>
          Lovelace PhD
          name Ada}
→
{} JSON
{
  "settings": {
    "active": true,
    "name": "Ada",
    "Lovelace": "PhD"
  }
}

To keep Ada Lovelace PhD as one value in packed mode, write it as "Ada Lovelace PhD". Canonical formatting chooses a layout from the decoded map rather than the input layout, while preserving the decoded values.

A map can also pack a compact nested value alongside its other members:

RON
data {
  count 3
  values [1 2 3]
  name Ada
}
→
Canonical RON
data {count 3
      name Ada
      values [1 2 3]}
→
{} JSON
{
  "data": {
    "count": 3,
    "values": [
      1,
      2,
      3
    ],
    "name": "Ada"
  }
}

In canonical output, every ordinary key/value pair has its own line, in sorted key order. The first item follows the opening brace, and subsequent items align beneath it. A tag group has its own line before ordinary members:

RON
record {
  field00 value00
  field01 value01
  field02 value02
  field03 value03
  field04 value04
  field05 value05
  field06 value06
  field07 value07
  field08 value08
  field09 value09
  field10 value10
}
→
Canonical RON
record {field00 value00
        field01 value01
        field02 value02
        field03 value03
        field04 value04
        field05 value05
        field06 value06
        field07 value07
        field08 value08
        field09 value09
        field10 value10}
→
{} JSON
{
  "record": {
    "field00": "value00",
    "field01": "value01",
    "field02": "value02",
    "field03": "value03",
    "field04": "value04",
    "field05": "value05",
    "field06": "value06",
    "field07": "value07",
    "field08": "value08",
    "field09": "value09",
    "field10": "value10"
  }
}

This also applies when all the ordinary members would fit beside the tags:

RON
user {<active> age 36 name Ada}
→
Canonical RON
user {<active>
      age 36
      name Ada}
→
{} JSON
{
  "user": {
    "active": true,
    "age": 36,
    "name": "Ada"
  }
}

This is the same map as the following compact form:

RON
{name "Ada Lovelace" age 36 active true}
→
Canonical RON
<active>
age 36
name Ada Lovelace
→
{} JSON
{
  "name": "Ada Lovelace",
  "age": 36,
  "active": true
}

Keys are strings, so punctuation and spaces may be part of a key when it is quoted. The empty string is also a valid key.

RON
{"full name" Ada "a:b" 1 "a,b" 2 "" empty}
→
Canonical RON
"" empty
"a,b" 2
a:b 1
"full name" Ada
→
{} JSON
{
  "full name": "Ada",
  "a:b": 1,
  "a,b": 2,
  "": "empty"
}

Keys are always read as strings. Number, boolean, and null spellings have no special meaning in key position.

RON
{null 456 true omg 1 one ^name text}
→
Canonical RON
1 one
^name text
null 456
true omg
→
{} JSON
{
  "null": 456,
  "true": "omg",
  "1": "one",
  "^name": "text"
}

Bare keys follow different token rules from bare values. A bare key runs to the whitespace before its value. It cannot contain commas or double quotes, or begin with [, ], {, }, or <. Brackets after the first character are part of the key. Parentheses do not introduce a RON container and may begin a bare key. Colons are ordinary characters in bare keys.

Each of these keys starts with a comma, double quote, bracket, brace, or tag-group opener and must be quoted. The quote is part of the second key's name; JSON-style escaping is accepted in the input, and canonical formatting chooses a fence:

RON
",name" 1
"\"name" 2
"[name" 3
"]name" 4
"{name" 5
"}name" 6
"<name" 7
→
Canonical RON
""\"name"" 2
",name" 1
"<name" 7
"[name" 3
"]name" 4
"{name" 5
"}name" 6
→
{} JSON
{
  ",name": 1,
  "\"name": 2,
  "[name": 3,
  "]name": 4,
  "{name": 5,
  "}name": 6,
  "<name": 7
}
RON
items[0] 1
/people/{id} {get x}
a:b colon
→
Canonical RON
/people/{id} {get x}
a:b colon
items[0] 1
→
{} JSON
{
  "items[0]": 1,
  "/people/{id}": {
    "get": "x"
  },
  "a:b": "colon"
}

Keys must be unique within a map. Repeated keys are errors, even when their values agree or one occurrence is written as a tag. Canonical formatting sorts members by key name, with boolean members collected into a tag group before the remaining members. These rules apply recursively to nested maps.

RON
{name Ada age 36 active true}
→
Canonical RON
<active>
age 36
name Ada
→
{} JSON
{
  "name": "Ada",
  "age": 36,
  "active": true
}