CODING CHALLENGE · N°40

JSON Parser (Recursive Descent)

Hard ParsingRecursionStrings

Write the parser behind every API and config file: turn a JSON string into native objects, arrays, numbers, strings, booleans and null — by hand, without the built-in JSON library. Recursive descent mirrors the grammar exactly. Solve it in Python or TypeScript, with hidden tests.

The problem

Implement parse_json(text): parse a JSON string into the language’s native values — objects (dicts/maps), arrays (lists), integers, strings, true/false, and nullwithout calling the built-in JSON parser. Support nesting and arbitrary whitespace between tokens. (For this exercise, numbers are integers and strings contain no escape sequences.)

EXAMPLE 1
Input text = '{"a":[1,2,true],"b":null}'
Output {"a": [1, 2, True], "b": None}
nested array and keywords
EXAMPLE 2
Input text = '[1, -2, 3]'
Output [1, -2, 3]
whitespace and a negative number
CONSTRAINTS
  • Do not use json.loads / JSON.parse — build the parser yourself.
  • Support objects, arrays, integers (with optional leading minus), unescaped strings, true/false/null, and whitespace.
  • Recursive descent: one function per grammar rule (value, object, array, string, number), sharing a position cursor.
SOLVE IT YOURSELF

Your turn — write it

Edit the stub, hit Run (or ⌘/Ctrl + Enter), and watch the hidden tests. Stuck? the hints are right above and Reveal solution is one click away.

YOUR TASK

Implement parse_json(text) with a position cursor and one function per rule. value() dispatches on the next non-space character; object()/array() loop over comma-separated items; string()/number() read literals. Return the fully built native value.

HINTS — 4 IDEAS
  1. Keep an index pos and a skip() that advances past spaces/tabs/newlines. Call it before reading each token.
  2. value(): peek the next char — "{" → object, "[" → array, "\"" → string, "t"/"f"/"n" → keyword, else a number.
  3. For an object, consume "{", then repeatedly read a string key, a ":", and a value, separated by ",", until "}". Arrays are the same without keys.
  4. A number runs from an optional "-" through the digits; stop at the first non-digit (a comma, bracket, or brace).
CPython · WebAssembly
Approach, complexity & discussion — open after you solve

The approach

Recursive descent. First tokenize the input (strings, numbers, punctuation, and the literals true/false/null), then parse a value by dispatching on the next token: { begins an object, [ an array, " a string, a digit a number — recursing for nested values. Track a position pointer and error on any unexpected token.

Complexity

Time O(n) in the input length; recursion depth follows the nesting depth.

Common mistakes

  • Not recursing for nested objects/arrays — the parser must call itself for values inside values.
  • Mishandling string escapes (\", \n, unicode) and surrounding whitespace.
  • Silently accepting malformed input instead of raising a clear parse error.

Where this shows up

Writing a JSON parser is the classic introduction to recursive-descent parsing, and the same tokenize-then-recurse structure underlies real programming-language parsers, database query engines, and config loaders. It demystifies exactly how any structured text becomes an in-memory data structure.

Finished this one? 0 / 75 Challenges done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More Challenges