# Help Jason format his JSON

Jason has a big JSON but it's unreadable, so he needs to prettify it.

## Formatting Spec

The JSON has 4 different types:

• Numbers; Just 0-9
• Strings; Double quoted " strings escaped with \
• Arrays; Delimited by [], with items separated by ,, items can be any of these types
• Objects; Delimited by {}, format is key: value where key is a string and value is any of these types

Spacing

• Arrays should have exactly one space after the commas between items
• Objects should have have just one space between the key and the value, after the :

Indentation

• Each nesting level is indented 2 more than the previous
• Each object key/value pair is always on its own line. Objects are indented
• An array is indented across multiple lines if it contains another array or object. Otherwise the array remains on one line

## Rules

• Built-ins which trivialize this task are not allowed.
• As always standard loopholes are disallowed

## Examples

[1,2,3]
[1, 2, 3]

{"a":1,"b":4}
{
"a": 1,
"b": 4
}

"foo"
"foo"

56
56

{"a":[{"b":1,"c":"foo"},{"d":[2,3,4,1], "a":["abc","def",{"d":{"f":[3,4]}}]}]}
{
"a": [
{
"b": 1,
"c": "foo"
},
{
"d": [2, 3, 4, 1],
"a": [
"abc",
"def",
{
"d": {
"f": [3, 4]
}
}
]
}
]
}

[2,["foo123 ' bar \" baz\\", [1,2,3]]]
[
2,
[
"foo123 ' bar \" baz\\",
[1, 2, 3]
]
]

[1,2,3,"4[4,5]"]
[1, 2, 3, "4[4,5]"]

[1,2,3,{"b":["{\"c\":[2,5,6]}",4,5]}]
[
1,
2,
3,
{
"b": ["{\"c\":[2,5,6]}", 4, 5]
}
]

• Are JSON parsing builtins allowed? – PurkkaKoodari Mar 1 '16 at 7:45
• Can objects/arrays be empty? Can we still print a space after commas in arrays if they split over multiple lines? – Martin Ender Mar 1 '16 at 8:40
• @MartinBüttner no, and yes – Downgoat Mar 1 '16 at 14:59
• @Pietu1998 hm, I'm going to say no – Downgoat Mar 1 '16 at 14:59
• Are language parser languages allowed? – Mama Fun Roll Mar 1 '16 at 16:27

## JavaScript (ES6), 368 bytes

f=(s,r=[],i='',j=i+'  ',a=[])=>s<'['?([,,r[0]]=s.match(s<'0'?/("(?:\\.|[^"])*")(.*)/:/(\d+)(.*)/))[1]:s<'{'?(_=>{for(;s<']';s=r[0])a.push(f(s.slice(1),r,j));r[0]=s.slice(1)})()||/\n/.test(a)?[
${j+a.join(, +j)}${i}]:[${a.join, }]:(_=>{for(a=[];s<'}';s=r[0])a.push(f(s.slice(1),r,j)+': '+f(r[0].slice(1),r,j));r[0]=s.slice(1)})()||{${j+a.join(,
+j)}
\${i}}


Less golfed:

function j(s, r=[], i='') { // default to no indentation
if (s < '0') { // string
let a = s.match(/("(?:\\.|[^"])*")(.*)/);
r[0] = a[2]; // pass the part after the string back to the caller
return a[1];
} else if (s < '[') { // number
let a = s.match(/(\d+)(.*)/);
r[0] = a[2]; // pass the part after the string back to the caller
return a[1];
} else if (s < '{') { // array
let a = [];
while (s < ']') { // until we see the end of the array
s = s.slice(1);
a.push(j(s, r, i + '  ')); // recurse with increased indentation
s = r[0]; // retrieve the rest of the string
}
r[0] = s.slice(1); // pass the part after the string back to the caller
if (/\n/.test(a.join())) { // array contained object
return '[\n  ' + i + a.join(',\n  ' + i) + '\n' + i + ']';
} else {
return '[' + a.join(', ') + ']';
}
} else { // object
let a = [];
while (s < '}') { // until we see the end of the object
s = s.slice(1);
let n = j(s, r, i + '  ');
s = r[0].slice(1);
let v = j(s, r, i + '  ');
a.push(n + ': ' + v);
s = r[0]; // retrieve the rest of the string
}
r[0] = s.slice(1); // pass the part after the string back to the caller
return '{\n  ' + i + a.join(',\n  ' + i) + '\n' + i + '}';
}
}