json1-1 Exercise 1: Predict, Then Let JSON.parse Decide ===================================================== Run with Node 24.18.0: node ex1.mjs (a "!!" in the left column marks a wrong prediction) --- ex1.mjs --- // Exercise 1: which of these are valid JSON? Predict first, then let JSON.parse decide. const bs = String.fromCharCode(92); const cases = [ // [label, text, I predicted valid?] ['plain object', '{"name":"Ada","age":36}', true], ['array of mixed types', '[1,"two",true,null,{"k":[]}]', true], ['top-level string', '"hello"', true], ['top-level number', '42', true], ['single quotes', "{'name':'Ada'}", false], ['unquoted key', '{name:"Ada"}', false], ['trailing comma', '[1,2,3,]', false], ['comment', '{"a":1} // note', false], ['undefined', '{"a":undefined}', false], ['NaN', '{"a":NaN}', false], ['leading zero', '{"a":007}', false], ['plus sign', '{"a":+5}', false], ['hex number', '{"a":0xFF}', false], ['escaped unicode', '{"a":"' + bs + 'u00e9"}', true], ['hex escape in string', '{"a":"' + bs + 'x41"}', false], ['raw tab in string', '{"a":"x\ty"}', false], ['empty text', '', false], ['two values in a row', '{"a":1}{"b":2}', false], ['duplicate keys', '{"a":1,"a":2}', true], ]; let wrong = 0; for (const [label, text, predicted] of cases) { let valid, note = ''; try { JSON.parse(text); valid = true; } catch (e) { valid = false; note = e.message; } const flag = valid === predicted ? ' ' : '!!'; if (valid !== predicted) wrong++; console.log(flag, label.padEnd(22), valid ? 'valid ' : 'INVALID', note); } console.log('\n' + cases.length + ' cases, predictions wrong: ' + wrong); Output: plain object valid array of mixed types valid top-level string valid top-level number valid single quotes INVALID Expected property name or '}' in JSON at position 1 (line 1 column 2) unquoted key INVALID Expected property name or '}' in JSON at position 1 (line 1 column 2) trailing comma INVALID Unexpected token ']', "[1,2,3,]" is not valid JSON comment INVALID Unexpected non-whitespace character after JSON at position 8 (line 1 column 9) undefined INVALID Unexpected token 'u', "{"a":undefined}" is not valid JSON NaN INVALID Unexpected token 'N', "{"a":NaN}" is not valid JSON leading zero INVALID Unexpected number in JSON at position 6 (line 1 column 7) plus sign INVALID Unexpected token '+', "{"a":+5}" is not valid JSON hex number INVALID Expected ',' or '}' after property value in JSON at position 6 (line 1 column 7) escaped unicode valid hex escape in string INVALID Bad escaped character in JSON at position 7 (line 1 column 8) raw tab in string INVALID Bad control character in string literal in JSON at position 7 (line 1 column 8) empty text INVALID Unexpected end of JSON input two values in a row INVALID Unexpected non-whitespace character after JSON at position 7 (line 1 column 8) duplicate keys valid 19 cases, predictions wrong: 0 Control (I changed ONE prediction on purpose: duplicate keys, true -> false): !! duplicate keys valid 19 cases, predictions wrong: 1 WHY THIS WORKS AS AN ANSWER --------------------------- Each text is built so the only question is the JSON grammar, and the prediction is written down before the run, so the counter measures what I knew rather than what I saw. All 19 predictions matched: the valid ones are the plain documents, top-level scalars, a unicode escape and duplicate keys; the invalid ones are every item on the forbidden list. The control run shows the counter can fail: flipping one prediction made it report 1 wrong and mark that row. Two details worth keeping: the escape texts are made with String.fromCharCode(92) (a backslash), because writing the escape inside an ordinary JavaScript string makes JavaScript, not JSON, interpret it; and "valid" here means Node's parser accepts it. Duplicate keys are valid to this parser, but RFC 8259 calls the result unpredictable, so another parser may disagree (exercise 2). The messages are Node 24's; other engines word them differently.