JSON Objects

Updated: August 8, 2026

Key-value pairs, nesting, and the design decisions — naming, duplicates, ordering — that determine whether your objects are pleasant or painful to consume.

Structure

An object is a comma-separated list of "key": value pairs between curly braces. It is JSON's primary building block — most documents are an object at the top level, containing more objects.

{
  "id": 101,
  "name": "Alice",
  "active": true,
  "address": {
    "city": "Chennai",
    "country": "IN"
  },
  "roles": ["admin", "editor"],
  "deletedAt": null
}

The rules are short:

  • Keys must be double-quoted strings. Not identifiers, not numbers, not single-quoted. {id: 1} is valid JavaScript and invalid JSON.
  • Values may be any of the seven JSON types, including further objects and arrays.
  • Pairs are separated by commas, and the last one must not be followed by one.
  • Keys are case sensitive. "Name" and "name" are different keys and may both appear.
  • {} is a valid, empty object — the standard way to say "no data" while keeping the shape.

Ordering Is Not Guaranteed

The specification defines an object as an unordered collection. Two documents whose keys appear in different sequences are equivalent, and a parser is entitled to hand them back in any order.

{ "a": 1, "b": 2 }
{ "b": 2, "a": 1 }     // the same object, by definition

In practice almost every implementation preserves document order, and both Python 3.7+ and modern JavaScript guarantee insertion order for their native map types. So code that relies on ordering usually works — until it meets a parser, a proxy, or a database JSON column that reorders keys. Treat any dependence on key order as a bug waiting for the right environment.

If order genuinely matters — a sequence of steps, a ranked list — use an array. Arrays are ordered, guaranteed.

Duplicate Keys Are Genuinely Dangerous

RFC 8259 says keys "SHOULD be unique" and then declines to say what happens when they are not. That leaves the behaviour implementation-defined.

{ "role": "user", "role": "admin" }

// JavaScript, Python, Go, PHP: last wins → "admin"
// Some parsers: first wins → "user"
// A few strict parsers: reject the document outright

The security consequence is real. If a gateway written in one language validates a request by reading the first occurrence, while the service behind it reads the last, an attacker can send a document that passes the check and executes with different values. This class of bug has appeared in production authorisation systems.

Nothing warns you. Because most parsers silently pick one, a document with duplicate keys looks entirely normal until the two ends of your system disagree. If you accept JSON from untrusted sources and security decisions depend on it, use a parser that can reject duplicates.

Naming Conventions

JSON imposes nothing here, which is why the ecosystem has three competing conventions:

{ "firstName": "Alice" }     // camelCase  — JavaScript, most public APIs
{ "first_name": "Alice" }    // snake_case — Python, Ruby, Rails, PostgreSQL
{ "FirstName": "Alice" }     // PascalCase — older .NET

None is more correct. What matters is that you pick one and hold it across the entire API, because a payload mixing userId and created_at forces every consumer to remember which fields follow which rule.

camelCase is the most common choice for public HTTP APIs, since JSON's heritage is JavaScript and consumers usually are too. snake_case is a reasonable default when your backend and database already use it — every serialiser can map between the two, as covered on the Java and Go pages.

Objects Versus Arrays of Objects

A recurring design decision: when you have a collection keyed by ID, do you use an object keyed by that ID, or an array of objects each carrying its ID?

// Object keyed by ID — O(1) lookup, no order
{
  "101": { "name": "Alice" },
  "102": { "name": "Bob" }
}

// Array of objects — ordered, iterable, self-describing
[
  { "id": 101, "name": "Alice" },
  { "id": 102, "name": "Bob" }
]

Prefer the array for API responses. It preserves order, it is trivially iterable in every language, it can carry duplicates or be empty without changing shape, and it is straightforward to describe in JSON Schema — an object with arbitrary numeric keys is not.

The keyed-object form has one strong use: a client-side lookup table built after fetching, where constant-time access by ID matters. Build it in the client; do not force it on the wire.

Nesting Depth

Objects nest without limit in the grammar. Every parser imposes one anyway, because unbounded recursion on hostile input is a denial-of-service vector — a document consisting of ten thousand opening braces can exhaust the call stack of a recursive-descent parser.

Practical depth limits sit in the hundreds to low thousands. You are unlikely to meet them accidentally, but you should not go anywhere near them deliberately either. Deep nesting is painful for consumers:

// Painful — every level is another null check
data.response.result.user.profile.contact.address.city

// Better — flatten to something addressable
data.user.address.city

Three or four levels is a reasonable ceiling for an API payload. Past that, consider whether the extra structure is carrying information or just mirroring your internal class hierarchy. When you must navigate something deep, a tree view is considerably easier than scrolling formatted text.

Accessing Nested Values Safely

// JavaScript — optional chaining stops at the first missing level
const city = data?.user?.address?.city ?? "unknown";

// Python — dict.get chains, or a default at each step
city = data.get("user", {}).get("address", {}).get("city", "unknown")

The reason this matters more in JSON than in typed languages is that a key's absence is indistinguishable from its presence until you look. An optional field the provider stopped sending will not raise an error at the boundary — it becomes undefined partway through your code, far from the cause. Validating with a schema at the boundary converts that into a clear failure at the right place.

Frequently Asked Questions

What is a JSON object?

An unordered collection of key-value pairs wrapped in curly braces. Every key must be a double-quoted string, and every value may be any JSON type — including another object, which is what allows arbitrary nesting.

Can a JSON object have duplicate keys?

The specification permits it but does not define what should happen, so behaviour varies by parser. Nearly all mainstream parsers keep the last occurrence and silently discard earlier ones. Because different systems may resolve it differently, duplicate keys have been used as a real attack vector against authorisation logic.

Are JSON object keys ordered?

Not according to the specification — objects are explicitly unordered. In practice most parsers preserve document order, and Python 3.7+ and modern JavaScript both do, but nothing requires it. Never write code whose correctness depends on key order.

Can a JSON key be a number or empty?

A key must be a string, so a bare number is invalid — but "123" in quotes is fine. An empty string "" is also a technically valid key, though it makes for confusing data and most style guides discourage it.

How deeply can JSON objects nest?

The specification sets no limit, but every parser does, to protect against stack exhaustion from maliciously deep input. Practical limits are usually in the hundreds to low thousands of levels. Deeply nested payloads are also painful to consume, so most APIs stay within three or four levels by design.

Related Resources

Related Resources