StackPractices
beginner By Mathias Paulenko

Parse JSON in Python, JavaScript and Java

Parse JSON strings into native objects in Python, JavaScript and Java — with error handling, streaming for large files and schema validation.

Topics: data

Overview

Almost every API response, webhook payload and config file you’ll touch arrives as JSON text. Parsing turns that text into native data — dicts, objects, POJOs — that your code can work with. Every mainstream language ships a solid way to do it, either built in or through a de-facto standard library.

Here you’ll find the idiomatic way to do it in Python, JavaScript and Java — plus the error cases that actually show up in production and what to do when a file won’t fit in memory. For the inverse operation, see Serialize and Deserialize Data; to check the parsed structure against a contract, see Validate JSON Schema.

When to Use

Use this recipe when:

  • Consuming a REST API that returns a JSON payload
  • Reading configuration or data files stored as .json
  • Deserializing webhook bodies or message-queue events
  • Converting a JSON string into typed objects in your domain model — see Data Validation Zod for schema-based parsing

When to Avoid

  • YAML or TOML for human-edited config: JSON lacks comments and multi-line strings. Use TOML or YAML for hand-authored configuration.
  • Binary data: JSON encodes binary as base64, inflating size by a third. Protobuf, MessagePack or CBOR serve binary-heavy payloads better.
  • Files too large for memory: switch to a streaming parser (Advanced section) — json.loads and JSON.parse need the whole input in memory.

Solution

Python

The built-in json module is all you need:

import json

raw = '{"name": "Ada", "age": 36, "skills": ["math", "code"]}'
data = json.loads(raw)

print(data["name"])      # "Ada"
print(data["skills"][0])  # "math"

json.loads accepts str, bytes or bytearray. To read from a file, use json.load (no “s”) on the open file object:

import json

with open("data.json", encoding="utf-8") as f:
    data = json.load(f)

Always handle malformed input — json.loads raises JSONDecodeError, which conveniently reports where the input went wrong:

import json

try:
    data = json.loads(raw)
except json.JSONDecodeError as e:
    print(f"Invalid JSON at line {e.lineno}, col {e.colno}: {e.msg}")
    raise

JavaScript

JSON.parse is built in. A reviver callback lets you convert values during parsing — the standard trick for reviving Date objects or fixing up numbers:

const raw = '{"name": "Ada", "age": 36, "skills": ["math", "code"]}';
const data = JSON.parse(raw);

console.log(data.name);       // "Ada"
console.log(data.skills[0]);  // "math"
const raw = '{"date": "2026-01-15", "count": "42"}';
const data = JSON.parse(raw, (key, value) => {
  if (key === 'count') return Number(value);
  if (key === 'date') return new Date(value);
  return value;
});
// data.date is a Date object, data.count is a number

Bad input throws a SyntaxError:

try {
  const data = JSON.parse(raw);
} catch (e) {
  if (e instanceof SyntaxError) {
    console.error('Invalid JSON:', e.message);
  }
  throw e;
}

Java

Java has no built-in parser, so reach for Jackson (com.fasterxml.jackson.core:jackson-databind) or Gson. Jackson gives you two styles: a navigable tree with readTree, or direct mapping onto a POJO with readValue:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;

ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(
    "{\"name\": \"Ada\", \"age\": 36}");

String name = node.get("name").asText(); // "Ada"
int age = node.get("age").asInt();        // 36
public class User {
    public String name;
    public int age;
    public List<String> skills;
}

User user = mapper.readValue(jsonString, User.class);
try {
    User user = mapper.readValue(json, User.class);
} catch (JsonProcessingException e) {
    log.error("Invalid JSON: {}", e.getOriginalMessage());
    throw new IllegalArgumentException("Bad request", e);
}

Explanation

The three languages land on the same idea with different ergonomics:

  • Python: json.loads on a string, json.load on a file object; both return dict/list. Useful hooks: object_hook to build custom objects per JSON object, and parse_int/parse_float to control numeric types (e.g. parse_float=Decimal for money).
  • JavaScript: JSON.parse returns plain objects and arrays; the optional reviver transforms each value bottom-up. Never parse JSON with eval() — it runs whatever it’s given.
  • Java: Jackson’s readTree gives a JsonNode tree for exploratory access; readValue binds straight to your classes and is usually what you want in typed code.

With the data parsed, Call a REST API shows how to fetch JSON over HTTP and Read and Write Files covers loading it from disk.

Variants

LanguageToolReturnsParse from file
Pythonjson (stdlib)dict / listjson.load(f)
JavaScriptJSON.parse() (builtin)Object / Arrayread file, then parse
JavaJackson / GsonJsonNode / POJOmapper.readValue(file, T.class)
Goencoding/json (stdlib)map[string]interface{} / structjson.NewDecoder(r).Decode(&v)
Rustserde_json crateValue / structserde_json::from_reader(r)
C#System.Text.Json (stdlib)JsonElement / classJsonSerializer.Deserialize<T>(stream)

Advanced: Streaming Large JSON Files

When a file won’t fit in memory, a streaming parser walks the document token by token instead of materializing the whole tree.

Python with ijson

import ijson

def process_large_json(file_path):
    with open(file_path, 'rb') as f:
        for item in ijson.items(f, 'items.item'):
            process(item)

Java with Jackson Streaming

try (JsonParser parser = mapper.getFactory().createParser(new File("large.json"))) {
    while (parser.nextToken() != JsonToken.END_OBJECT) {
        String fieldName = parser.getCurrentName();
        if ("items".equals(fieldName)) {
            parser.nextToken(); // START_ARRAY
            while (parser.nextToken() != JsonToken.END_ARRAY) {
                Item item = parser.readValueAs(Item.class);
                process(item);
            }
        }
    }
}

JavaScript with stream-json

const fs = require('fs');
const streamJson = require('stream-json');

const pipeline = fs.createReadStream('large.json')
  .pipe(streamJson.parser());

pipeline.on('data', (data) => {
  if (data.name === 'startArray') {
    // handle array start
  }
});

Advanced: JSON Schema Validation

Parsing tells you the text is well-formed JSON — not that it has the fields you expect. For APIs you own, validate the structure before trusting it:

import jsonschema

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer", "minimum": 0},
    },
    "required": ["name"],
}

jsonschema.validate(data, schema)  # raises ValidationError if invalid
const Ajv = require('ajv');
const ajv = new Ajv();
const validate = ajv.compile(schema);

if (!validate(data)) {
  console.error(validate.errors);
}

What Works

  • Always handle parse errors: wrap parsing in try/except (Python) or try/catch (JS/Java) — external JSON is untrusted.
  • Prefer typed models in Java: bind straight to POJOs with readValue() rather than walking a JsonNode by key strings.
  • Never use eval() in JavaScript: it executes arbitrary code — stick to JSON.parse().
  • Stream large payloads: use ijson (Python) or Jackson’s streaming JsonParser to avoid loading huge files into memory.
  • Validate against a schema: for APIs you own, validate with JSON Schema before trusting the structure.

Common Mistakes

  • Ignoring encoding: JSON is UTF-8; reading a file with the wrong encoding corrupts characters.
  • Catching nothing: malformed JSON throws — silently crashing the request handler is worse than a clear 400 response.
  • Number precision in JavaScript: integers beyond Number.MAX_SAFE_INTEGER lose precision; parse large IDs as strings.
  • Confusing loads and load: in Python, loads takes a string, load takes a file object.
  • Assuming keys are case-insensitive: they aren’t — Name and name are different fields.

Troubleshooting

  • “Expecting value: line 1 column 1” (Python) or “Unexpected token” (JS): the body usually isn’t JSON at all — an HTML error page, a 204 empty body, or a redirect. Dump the first bytes of the response to be sure.
  • Trailing commas, single quotes, unquoted keys: that’s a JS object literal or JSON5/JSONC, not JSON. Reach for a tolerant parser (json5, jsonc-parser) — or better, fix the producer.
  • UnicodeDecodeError or garbled characters: the file probably has a BOM or isn’t UTF-8. In Python, open(path, encoding="utf-8-sig") strips a BOM cleanly.
  • NaN / Infinity in the payload: they’re not valid JSON. JSON.parse rejects them; Python’s json.loads accepts them by default, so a payload that parses in dev may fail downstream.
  • Duplicate keys: most parsers silently keep the last value — in Python, object_pairs_hook can detect or reject them.
  • RecursionError / stack overflow: hostile or pathological nesting depth. Recent Jackson versions cap depth (default 1000) via StreamReadConstraints.

See Also

Frequently Asked Questions

How do I parse JSON from a file in Python?

Use json.load(f) (note load, not loads), passing an open file object: with open("data.json") as f: data = json.load(f). Pass encoding="utf-8" explicitly — JSON is UTF-8 by spec.

What is the Java equivalent of Python's json.loads?

Use ObjectMapper.readValue(String, Class<T>) when you have a typed class, readTree(String) when you don't. Jackson takes files and streams too: readValue(File, Class) and readValue(InputStream, Class).

Should I validate JSON before parsing it?

You can't validate the structure before parsing — validation happens after, on the parsed data. For external sources, defensive parsing with try/catch is the minimum; for APIs you control, validate with JSON Schema after parsing.

How do I handle large integers in JavaScript JSON?

JavaScript numbers are 64-bit floats, so integers beyond Number.MAX_SAFE_INTEGER (2^53 - 1) lose precision. Use a reviver function to parse large numbers as strings: JSON.parse(raw, (k, v) => typeof v === 'number' && v > Number.MAX_SAFE_INTEGER ? String(v) : v). Or reach for BigInt via a custom parser.

How do I parse JSON with comments (JSONC)?

Standard JSON doesn't allow comments — you'll hit this mostly in VS Code settings or hand-edited config, where JSONC is common. Parse it with jsonc-parser (npm) or json5 (Python/JS), or strip comments first. Whatever you do, don't reach for eval().

What is the difference between JSON.parse and JSON.stringify?

JSON.parse takes a JSON string and gives back a JavaScript value; JSON.stringify runs the opposite direction. They're inverses, though stringify quietly drops undefined values and functions.

How do I parse JSON streams (NDJSON)?

NDJSON (newline-delimited JSON) carries one object per line — common in logs and streaming exports. Python: [json.loads(line) for line in f if line.strip()]. Node.js: readline plus JSON.parse. Java: a line-delimited reader plus ObjectMapper.readValues().

What is JSON5 and should I use it?

JSON5 is a superset of JSON that allows comments, trailing commas, unquoted keys, and multi-line strings — handy for human-authored config files, and the json5 library parses it. For API payloads, though, stick to standard JSON: interoperability wins.

How do I parse JSON in Go?

Use encoding/json from the stdlib: var v MyStruct; err := json.Unmarshal([]byte(raw), &v). For streaming, use json.NewDecoder(r).Decode(&v). Go requires exported struct fields with json:"fieldName" tags for mapping.

What is the maximum JSON string size?

The spec doesn't set one — practical limits come from your parser and memory. Python's json will happily parse up to available memory; JSON.parse tops out at the JS heap limit (typically 2-4 GB). Past ~1 GB, go streaming.