Skip to content
j-brooke edited this page Oct 28, 2022 · 17 revisions

FracturedJson

About

FracturedJson is a family of utilities that format JSON data in a way that's easy for humans to read, but fairly compact. Arrays and objects are written on single lines, as long as they're neither too long nor too complex. When several such lines are similar in structure, they're written with fields aligned like a table. Long arrays are written with multiple items per line across multiple lines.

There are lots of settings available to control the output, but for the most part you can ignore them. FracturedJson produces nice-looking output from any set of JSON data automatically.

You can try it out with the browser formatter page. It's also available as a .NET library, a JavaScript/Typescript package, and a Visual Studio Code extension. There's also a Python port by masaccio.

Here's a sample of output using default settings. See the Options page for examples of what you different settings do.

{
    "SimpleArray": [
          2,   3,   5,   7,  11,  13,  17,  19,  23,  29,  31,  37,  41,  43,  47,  53,  59,  61,
         67,  71,  73,  79,  83,  89,  97, 101, 103, 107, 109, 113
    ],
    "ObjectColumnsArrayRows": {
        "Katherine": ["blue"      , "lightblue", "black"       ],
        "Logan"    : ["yellow"    , "blue"     , "black", "red"],
        "Erik"     : ["red"       , "purple"                   ],
        "Jean"     : ["lightgreen", "yellow"   , "black"       ]
    },
    "ArrayColumnsObjectRows": [
        { "type": "turret"   , "hp": 400, "loc": {"x": 47, "y":  -4}, "flags": "S"   },
        { "type": "assassin" , "hp":  80, "loc": {"x": 12, "y":   6}, "flags": "Q"   },
        { "type": "berserker", "hp": 150, "loc": {"x":  0, "y":   0}                 },
        { "type": "pittrap"  ,            "loc": {"x": 10, "y": -14}, "flags": "S,I" }
    ],
    "ComplexArray": [
        [19,  2],
        [ 3,  8],
        [14,  0],
        [ 9,  9],
        [ 9,  9],
        [ 0,  3],
        [10,  1],
        [ 9,  1],
        [ 9,  2],
        [ 6, 13],
        [18,  5],
        [ 4, 11],
        [12,  2]
    ]
}

Optionally, comments can be preserved. Comments aren't allowed by the official JSON standard, but they're ubiquitous, so it's nice to have the option. FracturedJson tries to keep the comments together with whatever elements they seem to relate to.

{
    /*
     * Multi-line comments
     * are fun!
     */
    "NumbersWithHex": [
          254 /*00FE*/,  1450 /*5AA*/ ,     0 /*0000*/, 36000 /*8CA0*/,    10 /*000A*/,
          199 /*00C7*/, 15001 /*3A99*/,  6540 /*198C*/
    ],
    /* Elements are keen */
    "Elements": [
        { /*Carbon*/   "Symbol": "C" , "Number":  6, "Isotopes": [11, 12, 13, 14] },
        { /*Oxygen*/   "Symbol": "O" , "Number":  8, "Isotopes": [16, 18, 17    ] },
        { /*Hydrogen*/ "Symbol": "H" , "Number":  1, "Isotopes": [ 1,  2,  3    ] },
        { /*Iron*/     "Symbol": "Fe", "Number": 26, "Isotopes": [56, 54, 57, 58] }
        // Not a complete list...
    ],

    "Beatles Songs": [
        "Taxman"        ,  // George
        "Hey Jude"      ,  // Paul
        "Act Naturally" ,  // Ringo
        "Ticket To Ride"   // John
    ]
}

Motivation

Most JSON libraries give you a choice between two formatting options. Minified JSON is very efficient, but difficult for a person to read.

{"AttackPlans":[{"TeamId":1,"Spawns":[{"Time":0.0,"UnitType":"Grunt","SpawnPointIndex":0},{"Time":0.0,"UnitType":"Grunt","SpawnPointIndex":0},{"Time":0.0,"UnitType":"Grunt","SpawnPointIndex":0}]}],"DefensePlans":[{"TeamId":2,"Placements":[{"UnitType":"Archer","Position":[41,7]},{"UnitType":"Pikeman","Position":[40,7]},{"UnitType":"Barricade","Position":[39,7]}]}]}

Most beautified/indented JSON, on the other hand, is too spread out, often making it difficult to read as well.

{
    "AttackPlans": [
        {
            "TeamId": 1,
            "Spawns": [
                {
                    "Time": 0,
                    "UnitType": "Grunt",
                    "SpawnPointIndex": 0
                },
                {
                    "Time": 0,
                    "UnitType": "Grunt",
                    "SpawnPointIndex": 0
                },
                {
                    "Time": 0,
                    "UnitType": "Grunt",
                    "SpawnPointIndex": 0
                }
            ]
        }
    ],
    "DefensePlans": [
        {
            "TeamId": 2,
            "Placements": [
                {
                    "UnitType": "Archer",
                    "Position": [
                        41,
                        7
                    ]
                },
                {
                    "UnitType": "Pikeman",
                    "Position": [
                        40,
                        7
                    ]
                },
                {
                    "UnitType": "Barricade",
                    "Position": [
                        39,
                        7
                    ]
                }
            ]
        }
    ]
}

FracturedJson tries to format data like a person would. Containers are kept to a single line as long as they're not too complex, and not too long. If several successive inline arrays or objects are similar enough, they will be formatted as a table.

{
    "AttackPlans": [
        {
            "TeamId": 1,
            "Spawns": [
                {"Time": 0, "UnitType": "Grunt", "SpawnPointIndex": 0},
                {"Time": 0, "UnitType": "Grunt", "SpawnPointIndex": 0},
                {"Time": 0, "UnitType": "Grunt", "SpawnPointIndex": 0}
            ]
        }
    ],
    "DefensePlans": [
        {
            "TeamId": 2,
            "Placements": [
                { "UnitType": "Archer"   , "Position": [41, 7] },
                { "UnitType": "Pikeman"  , "Position": [40, 7] },
                { "UnitType": "Barricade", "Position": [39, 7] }
            ]
        }
    ]
}

How It Works

FracturedJson uses four types of formatting: inlined, compact multiline array, table and expanded.

Inlined

When possible, sections of the document are written inlined, as long as that doesn't make them too long or too complex (as determined by the settings). "Complexity" refers to how deeply nested an array or object's contents are.

Example:

{ "UnitType": "Archer", "Position": [41, 7] }

Compact Multiline Array

The next option, for arrays, is to write them with multiple items per line, across multiple lines. Again, settings determine how long and how complex them may be.

Example:

[
    [19,  2], [ 3,  8], [14,  0], [ 9,  9], [ 9,  9], [ 0,  3], [10,  1],
    [ 9,  1], [ 9,  2], [ 6, 13], [18,  5], [ 4, 11], [12,  2]
]

Table

If an array or object contains inlineable items of the same type, they can be formatted in a tabular format. With enough room, all fields at any depth are lined up (and reordered if necessary).

Example:

{
    "Rect" : { "position": {"x": -44, "y":  3.40        }, "color": [0, 255, 255] },
    "Point": { "position": {          "y": 22.00, "z": 3}                         },
    "Oval" : { "position": {"x": 140, "y":  0.04        }, "color": "#7f3e96"     },
    "Plane": { "position": null                          , "color": [0, 64, 64]   }
}

If the settings don't allow table formatting at full width, the inner-most elements will be collapsed while the outer elements remain aligned.

{
    "Rect" : { "position": {"x": -44, "y": 3.4} , "color": [0, 255, 255] },
    "Point": { "position": {"y": 22, "z": 3}                             },
    "Oval" : { "position": {"x": 140, "y": 0.04}, "color": "#7f3e96"     },
    "Plane": { "position": null                 , "color": [0, 64, 64]   }
}

Expanded

If none of those options work for an element, it will be written across multiple lines, with child items indented and starting on its own line.

Example:

[
    {
        "type": "turret",
        "hp": 400,
        "loc": {"x": 47, "y": -4},
        "flags": ["stationary"]
    },
    {
        "type": "assassin",
        "hp": 80,
        "loc": {"x": 102, "y": 6},
        "flags": ["stealth"]
    },
    { "type": "berserker", "hp": 150, "loc": {"x": 0, "y": 0} },
    {
        "type": "pittrap",
        "loc": {"x": 10, "y": -14},
        "flags": ["invulnerable", "stationary"]
    }
]

Discussions

If you have any questions, comments, requests, advise, or whatever, feel free to check out the discussions area.

Clone this wiki locally