JSON for Developers: Syntax, Parsing, and Best Practices

Published March 2026 · 15 min read

1. What is JSON?

JSON (JavaScript Object Notation) is a lightweight, text-based data interchange format that has become the backbone of modern web development. Originally specified by Douglas Crockford in the early 2000s, JSON was designed to be a minimal, readable format that both humans and machines could parse effortlessly. Despite its name referencing JavaScript, JSON is language-independent and supported by virtually every programming language in use today.

Before JSON, XML was the dominant data interchange format. While XML is powerful and extensible, it comes with significant verbosity. Consider a simple representation of a user:

XML vs JSON Comparison

Here is the same data represented in XML:

<user>
  <name>Alice</name>
  <age>30</age>
  <email>alice@example.com</email>
  <roles>
    <role>admin</role>
    <role>editor</role>
  </roles>
</user>

And the same data in JSON:

{
  "name": "Alice",
  "age": 30,
  "email": "alice@example.com",
  "roles": ["admin", "editor"]
}

The JSON version is shorter, easier to read, and maps directly to data structures in most programming languages. This simplicity is precisely why JSON replaced XML as the preferred format for APIs, configuration files, and data storage in web applications.

Fun fact: JSON is defined by two standards simultaneously — ECMA-404 and RFC 8259. The entire specification fits on a single printed page, making it one of the simplest standards in computing.

2. JSON Syntax Rules

JSON has strict syntax rules. Unlike JavaScript, which is forgiving about formatting, JSON parsers will reject data that does not conform exactly to the specification. Understanding these rules is essential to avoid frustrating parsing errors.

Data Types

JSON supports exactly six data types. Every value in a JSON document must be one of:

{
  "string": "Hello, World!",
  "number_integer": 42,
  "number_float": 3.14,
  "number_scientific": 2.998e8,
  "boolean_true": true,
  "boolean_false": false,
  "null_value": null,
  "object": { "key": "value" },
  "array": [1, "two", true, null]
}

String Rules

Strings in JSON must use double quotes. Single quotes, backticks, and unquoted strings are all invalid. Special characters must be escaped with a backslash:

{
  "valid": "This is a valid string",
  "with_escape": "She said \"hello\"",
  "with_newline": "Line 1\nLine 2",
  "with_tab": "Column1\tColumn2",
  "with_unicode": "Emoji: \u2764",
  "with_backslash": "C:\\Users\\Documents"
}

Warning: A common mistake is using single quotes: {'name': 'Alice'} — this is valid Python but invalid JSON. Always use double quotes.

Number Rules

JSON numbers follow strict formatting. There are several things you cannot do with numbers in JSON:

No Comments, No Trailing Commas

Two features that developers frequently wish for in JSON are comments and trailing commas. Neither is allowed. The JSON specification intentionally excludes comments to prevent them from being used as parsing directives, which had been a problem with XML. Trailing commas were excluded to keep the grammar unambiguous and the parser logic simple.

// INVALID JSON - do not do this
{
  "name": "Alice", // this is a comment
  "age": 30,       // another comment
  "roles": [
    "admin",
    "editor",  // trailing comma here
  ]
}

Tip: If you need comments in configuration files, consider using JSONC (JSON with Comments), which is supported by VS Code and TypeScript tsconfig.json files, or use a "_comment" key as a convention.

3. JSON Objects and Arrays

Objects

A JSON object is an unordered set of key-value pairs enclosed in curly braces. Keys must be strings (double-quoted), and values can be any valid JSON data type. Keys within the same object should be unique — while the spec does not strictly forbid duplicates, most parsers will silently use the last value, leading to unpredictable behavior.

{
  "id": 1,
  "name": "Alice Johnson",
  "email": "alice@example.com",
  "isActive": true,
  "address": {
    "street": "123 Main St",
    "city": "Springfield",
    "state": "IL",
    "zip": "62701"
  }
}

Arrays

A JSON array is an ordered collection of values enclosed in square brackets. Unlike arrays in many programming languages, JSON arrays can hold mixed types — strings, numbers, objects, other arrays, and null values can all coexist in a single array.

{
  "tags": ["javascript", "web", "api"],
  "matrix": [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
  "mixed": [42, "hello", true, null, { "nested": "object" }]
}

Real-World Example: API Response

Here is a realistic API response that demonstrates nesting objects and arrays together:

{
  "status": "success",
  "data": {
    "users": [
      {
        "id": 1,
        "name": "Alice Johnson",
        "email": "alice@example.com",
        "roles": ["admin", "editor"],
        "profile": {
          "avatar": "https://example.com/avatars/alice.png",
          "bio": "Full-stack developer",
          "joinedAt": "2024-01-15T08:30:00Z"
        }
      },
      {
        "id": 2,
        "name": "Bob Smith",
        "email": "bob@example.com",
        "roles": ["viewer"],
        "profile": {
          "avatar": null,
          "bio": "Frontend enthusiast",
          "joinedAt": "2024-03-22T14:00:00Z"
        }
      }
    ],
    "pagination": {
      "page": 1,
      "perPage": 20,
      "total": 2,
      "totalPages": 1
    }
  }
}

4. Working with JSON in JavaScript

JavaScript provides two built-in methods for working with JSON: JSON.parse() and JSON.stringify(). These are the workhorses of JSON handling in any JavaScript or TypeScript application.

JSON.parse() — String to Object

JSON.parse() takes a JSON string and converts it into a JavaScript value. If the string is not valid JSON, it throws a SyntaxError. Always wrap it in a try-catch block:

const jsonString = '{"name": "Alice", "age": 30}';

try {
  const user = JSON.parse(jsonString);
  console.log(user.name); // "Alice"
  console.log(user.age);  // 30
} catch (error) {
  console.error("Invalid JSON:", error.message);
}

The Reviver Function

JSON.parse() accepts an optional second argument called the "reviver" — a function that transforms each key-value pair during parsing. This is particularly useful for converting date strings into Date objects:

const json = '{"name": "Alice", "createdAt": "2024-01-15T08:30:00Z"}';

const data = JSON.parse(json, (key, value) => {
  // Convert ISO date strings to Date objects
  if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
    return new Date(value);
  }
  return value;
});

console.log(data.createdAt instanceof Date); // true
console.log(data.createdAt.getFullYear());   // 2024

JSON.stringify() — Object to String

JSON.stringify() converts a JavaScript value to a JSON string. It accepts up to three arguments: the value to serialize, an optional replacer, and an optional space parameter for formatting.

const user = {
  name: "Alice",
  age: 30,
  roles: ["admin", "editor"]
};

// Compact output (default)
JSON.stringify(user);
// '{"name":"Alice","age":30,"roles":["admin","editor"]}'

// Pretty-printed with 2-space indentation
JSON.stringify(user, null, 2);
// {
//   "name": "Alice",
//   "age": 30,
//   "roles": [
//     "admin",
//     "editor"
//   ]
// }

The Replacer Function

The replacer function lets you filter or transform values during serialization. Returning undefined from the replacer removes that key from the output:

const user = {
  name: "Alice",
  password: "s3cret!",
  email: "alice@example.com",
  age: 30
};

// Remove sensitive fields
const safeJson = JSON.stringify(user, (key, value) => {
  if (key === "password") return undefined;
  return value;
}, 2);

// Result: { "name": "Alice", "email": "alice@example.com", "age": 30 }

// You can also pass an array of keys to include
const partial = JSON.stringify(user, ["name", "email"], 2);
// Result: { "name": "Alice", "email": "alice@example.com" }

The toJSON() Method

Objects can define a toJSON() method to control how they are serialized. When JSON.stringify() encounters an object with this method, it calls it and serializes the return value instead:

class User {
  constructor(name, email, password) {
    this.name = name;
    this.email = email;
    this.password = password;
  }

  toJSON() {
    return {
      name: this.name,
      email: this.email
      // password is intentionally excluded
    };
  }
}

const user = new User("Alice", "alice@example.com", "s3cret!");
JSON.stringify(user, null, 2);
// { "name": "Alice", "email": "alice@example.com" }

5. Common JSON Errors and How to Fix Them

JSON parsing errors are among the most frequently encountered bugs in web development. Here are the most common mistakes and how to resolve them.

Trailing Commas

// INVALID — trailing comma after "editor"
{
  "roles": ["admin", "editor",]
}

// VALID — remove the trailing comma
{
  "roles": ["admin", "editor"]
}

Single Quotes

// INVALID — single quotes are not allowed
{'name': 'Alice'}

// VALID — use double quotes
{"name": "Alice"}

Unquoted Keys

// INVALID — keys must be quoted strings
{name: "Alice", age: 30}

// VALID — wrap keys in double quotes
{"name": "Alice", "age": 30}

NaN, Infinity, and undefined

These JavaScript values have no representation in JSON. JSON.stringify() handles them differently depending on context:

JSON.stringify({ a: NaN, b: Infinity, c: undefined });
// '{"a":null,"b":null}'
// NaN and Infinity become null; undefined keys are omitted

JSON.stringify([NaN, Infinity, undefined]);
// '[null,null,null]'
// In arrays, all three become null

Date Handling

JSON has no native Date type. When you stringify a Date object, JavaScript calls its toISOString() method, producing a string. When you parse it back, you get a string — not a Date object:

const data = { createdAt: new Date("2024-01-15") };

const json = JSON.stringify(data);
// '{"createdAt":"2024-01-15T00:00:00.000Z"}'

const parsed = JSON.parse(json);
console.log(typeof parsed.createdAt); // "string" (not a Date!)

// Fix: use a reviver function (see Section 4 above)

Warning: Always validate JSON from external sources. Never assume it will be well-formed or match the shape you expect. Use try/catch around JSON.parse() and validate the resulting structure.

6. JSON Schema

JSON Schema is a declarative language for describing the structure of JSON data. It lets you define what fields are required, what types they should be, constraints like minimum/maximum values, and string patterns. This is invaluable for API contracts, form validation, and documentation.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["name", "email"],
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 150
    },
    "roles": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["admin", "editor", "viewer"]
      },
      "minItems": 1,
      "uniqueItems": true
    }
  },
  "additionalProperties": false
}

Libraries like ajv (JavaScript), jsonschema (Python), and built-in support in frameworks like NestJS make it straightforward to validate incoming data against a schema at runtime. For TypeScript developers, tools like zod and yup provide similar validation with type inference, though they are not JSON Schema per se.

Why it matters: JSON Schema serves as a single source of truth for API contracts. Both the frontend and backend can validate against the same schema, catching data issues before they cause runtime errors. OpenAPI (Swagger) specifications use JSON Schema to describe request and response bodies.

7. JSON vs Other Formats

JSON is not the only data interchange format available. Depending on your use case, another format might be more appropriate. Here is a comparison of JSON with the most popular alternatives:

FeatureJSONXMLYAMLProtobuf
Human-readableYesYesVeryNo (binary)
File sizeSmallLargeSmallSmallest
CommentsNoYesYesYes (in .proto)
SchemaJSON SchemaXSD/DTDNo standardBuilt-in
Parse speedFastSlowMediumFastest
Best forAPIs, configDocuments, SOAPConfig filesMicroservices, RPC

JSON vs YAML

YAML is a superset of JSON, meaning all valid JSON is also valid YAML. YAML uses indentation instead of braces, supports comments, and is generally more pleasant to write by hand. It is the standard for Kubernetes manifests, Docker Compose files, and CI/CD configurations (GitHub Actions, GitLab CI). However, YAML's flexibility is also its weakness — indentation errors are common and hard to spot, and features like anchors and aliases can create security vulnerabilities.

JSON vs Protocol Buffers and MessagePack

Protocol Buffers (Protobuf) and MessagePack are binary formats that offer significantly smaller payload sizes and faster serialization than JSON. Protobuf requires a schema definition (.proto file) and code generation, making it ideal for microservice-to-microservice communication where performance matters. MessagePack is a binary format that is compatible with JSON structure but smaller. Use JSON for human-facing APIs and debugging; use binary formats when bandwidth and latency are critical.

8. Performance Tips

JSON parsing and serialization performance matters at scale. Here are practical techniques for optimizing JSON handling:

Performance insight: V8 (Chrome/Node.js) optimizes JSON.parse() heavily. In fact, for large objects, JSON.parse('{...}') can be faster than writing the equivalent JavaScript object literal, because the JSON parser is simpler and more optimized than the full JavaScript parser.

9. JSON in APIs (REST & GraphQL)

JSON is the default data format for both REST and GraphQL APIs. Understanding the conventions around JSON in APIs will help you build and consume them effectively.

Content-Type Header

When sending or receiving JSON, always set the appropriate headers:

// Sending JSON in a fetch request
const response = await fetch("https://api.example.com/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify({
    name: "Alice",
    email: "alice@example.com"
  })
});

const data = await response.json();

Error Response Conventions

A consistent error response format makes APIs easier to consume. Here is a widely-adopted pattern:

// Success response
{
  "status": "success",
  "data": { "id": 1, "name": "Alice" }
}

// Error response
{
  "status": "error",
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid email address",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email format"
      }
    ]
  }
}

// Paginated response
{
  "status": "success",
  "data": [...],
  "meta": {
    "page": 1,
    "perPage": 20,
    "total": 150,
    "totalPages": 8
  },
  "links": {
    "self": "/api/users?page=1",
    "next": "/api/users?page=2",
    "last": "/api/users?page=8"
  }
}

GraphQL and JSON

GraphQL APIs also use JSON for their request and response bodies. The query itself is sent as a JSON string, and the response follows a standard structure with data and optional errors fields:

// GraphQL request body
{
  "query": "query { user(id: 1) { name email roles } }",
  "variables": {}
}

// GraphQL response
{
  "data": {
    "user": {
      "name": "Alice",
      "email": "alice@example.com",
      "roles": ["admin"]
    }
  }
}

10. Security Considerations

JSON data from untrusted sources can be a vector for attacks. Here are the key security concerns to be aware of:

Never Use eval() to Parse JSON

In the early days of JavaScript, developers sometimes used eval() to parse JSON strings. This is extremely dangerous because eval() executes arbitrary JavaScript code. Always use JSON.parse(), which only parses data and cannot execute code.

// DANGEROUS — never do this
const data = eval("(" + jsonString + ")");

// SAFE — always use JSON.parse
const data = JSON.parse(jsonString);

Prototype Pollution

Prototype pollution occurs when an attacker injects properties like __proto__ or constructor into JSON data, potentially modifying the prototype chain of all JavaScript objects. While JSON.parse() itself is safe, libraries that recursively merge JSON objects (like lodash's _.merge()) can be vulnerable:

// Malicious JSON payload
{
  "__proto__": {
    "isAdmin": true
  }
}

// After a vulnerable merge operation, ALL objects would have isAdmin: true
const user = {};
console.log(user.isAdmin); // true — prototype polluted!

// Prevention: use Object.create(null) for dictionaries,
// or validate/strip __proto__ and constructor keys

Input Validation and Sanitization

Always validate JSON data from external sources before using it:

Security rule of thumb: Never trust JSON from client-side or third-party sources. Always parse with JSON.parse(), validate against a schema, and sanitize values before rendering them in the DOM or using them in database queries.

11. Try It Yourself

The best way to learn JSON is by working with it hands-on. Format, validate, and explore JSON data using our free online tool. Paste in your JSON to instantly format it with syntax highlighting, detect errors, and navigate nested structures with ease.

JSON Formatter & Validator

Paste your JSON to format, validate, and explore it instantly. Catch syntax errors, pretty-print with proper indentation, and minify for production.

Open JSON Formatter