json1-1 Exercise 2: The Same Twelve Inputs in Node and Python ============================================================= --- ex2.mjs (Node 24.18.0) --- const bs = String.fromCharCode(92); const cases = [ ['NaN', '{"a":NaN}'], ['Infinity', '[Infinity]'], ['1e400', '[1e400]'], ['big integer', '[12345678901234567890]'], ['2^53 + 1', '[9007199254740993]'], ['negative zero', '[-0]'], ['1.0', '[1.0]'], ['duplicate keys', '{"a":1,"a":2}'], ['lone surrogate', '["' + bs + 'ud83d"]'], ['surrogate pair', '["' + bs + 'ud83d' + bs + 'ude00"]'], ['BOM first', '[1]'], ['deep nesting 10000', '['.repeat(10000) + ']'.repeat(10000)], ]; for (const [label, text] of cases) { let out; try { const v = JSON.parse(text); out = label === 'deep nesting 10000' ? 'ok' : label === 'lone surrogate' ? 'ok, code ' + v[0].charCodeAt(0).toString(16) : label === 'surrogate pair' ? 'ok, code ' + v[0].codePointAt(0).toString(16) : 'ok ' + (Object.is(Object.values(v)[0], -0) ? '-0' : JSON.stringify(v) === undefined ? '' : String(Array.isArray(v) ? v[0] : v.a)) + ' (' + typeof (Array.isArray(v) ? v[0] : v.a) + ')'; } catch (e) { out = 'ERROR ' + e.constructor.name; } console.log(label.padEnd(20) + out); } --- ex2.py (Python 3.14.0) --- import json bs = chr(92) cases = [ ('NaN', '{"a":NaN}'), ('Infinity', '[Infinity]'), ('1e400', '[1e400]'), ('big integer', '[12345678901234567890]'), ('2^53 + 1', '[9007199254740993]'), ('negative zero', '[-0]'), ('1.0', '[1.0]'), ('duplicate keys', '{"a":1,"a":2}'), ('lone surrogate', '["' + bs + 'ud83d"]'), ('surrogate pair', '["' + bs + 'ud83d' + bs + 'ude00"]'), ('BOM first', '[1]'), ('deep nesting 10000', '[' * 10000 + ']' * 10000), ] for label, text in cases: try: v = json.loads(text) first = v[0] if isinstance(v, list) else v['a'] if label == 'deep nesting 10000': out = 'ok' elif label == 'lone surrogate': out = 'ok, code %x' % ord(first[0]) elif label == 'surrogate pair': out = 'ok, code %x' % ord(first[0]) else: out = 'ok %s (%s)' % (repr(first), type(first).__name__) except RecursionError: out = 'ERROR RecursionError' except Exception as e: out = 'ERROR ' + type(e).__name__ print(label.ljust(20) + out) Output of node ex2.mjs: NaN ERROR SyntaxError Infinity ERROR SyntaxError 1e400 ok Infinity (number) big integer ok 12345678901234567000 (number) 2^53 + 1 ok 9007199254740992 (number) negative zero ok -0 (number) 1.0 ok 1 (number) duplicate keys ok 2 (number) lone surrogate ok, code d83d surrogate pair ok, code 1f600 BOM first ERROR SyntaxError deep nesting 10000 ok Output of py ex2.py: NaN ok nan (float) Infinity ok inf (float) 1e400 ok inf (float) big integer ok 12345678901234567890 (int) 2^53 + 1 ok 9007199254740993 (int) negative zero ok 0 (int) 1.0 ok 1.0 (float) duplicate keys ok 2 (int) lone surrogate ok, code d83d surrogate pair ok, code 1f600 BOM first ERROR JSONDecodeError deep nesting 10000 ok Inputs where the printed result differs: 6 NaN, Infinity, big integer, 2^53 + 1, negative zero, 1.0 (Judged by reading the two outputs: 1e400 prints Infinity in Node and inf in Python, the same value, and the BOM line names a different error class but both fail, so those two count as the same.) WHY THIS WORKS AS AN ANSWER --------------------------- The escape texts are built from a backslash character code rather than typed into a string literal, so each language's parser, not the language itself, handles them. The comparison found real differences on six inputs: NaN and Infinity (Python accepts, Node rejects), a 20-digit integer and 2^53+1 (Python exact, Node rounds), -0 (Node keeps the sign, Python reads an int 0) and 1.0 (a float in Python, plain 1 in Node). The same text can therefore give different data in two correct programs. Duplicate keys, lone and paired surrogates, the BOM and 10,000-deep nesting behaved the same in these two parsers, which does not mean they behave the same everywhere: I tested only these two. For the sign of zero and the float/int split the comparison depends on how I printed the value (Node's Object.is, Python's repr), so check what you compare.