Welcome to Part 3 of our JSON tutorial series. In the previous parts, we covered the basic syntax in part 1 and best practices and conventions in part 2. Now, we’ll explore how JSON’s simple building blocks—objects and arrays—can be combined to create sophisticated data structures capable of representing complex information and hierarchies.
Beyond Simple Key/Value Pairs
While simple key/value pairs are the foundation, real-world data often involves relationships and groupings that require more than a flat structure. JSON excels at modeling these complexities through nesting.
Topics covered in this article:
- Nested Objects
- Arrays of Objects
- Arrays within Objects and Objects within Arrays
- Choosing the Right Structure: Flat vs. Nested
- Organizing Large JSON Documents
- Key Collisions and Uniqueness
Nested Objects
A nested object is simply an object that is the value of a key within another object. This allows you to create hierarchical structures, grouping related attributes together.2
Definition and Purpose
Nesting objects is useful for:
- Representing “has-a” relationships: For example, a user object “has an” address object.
- Grouping related data: Encapsulating a set of configuration options within a settings object.
- Improving readability: Breaking down complex objects into smaller, more manageable sub-objects.
Examples
Consider a user object that includes contact information. Instead of having flat email and phone keys, we can nest them within a contact object:
{
"userId": "user123",
"username": "alicew",
"contact": {
"email": "alice.w@example.com",
"phone": "555-0102",
"address": {
"street": "123 Wonderland Ave",
"city": "Anystate",
"zipCode": "12345"
}
}
}
Here, contact is a nested object, and address is nested within contact.
JavaScript accessing a JSON property
Both the JavaScript dot notation and bracket notation will get a property value from the userData JSON :
let userData = { /*...the JSON above... */ };
let myUserId = userData.userId; // "user123"
let myUserName = userData["username"]; // "alicew"
JavaScript accessing Nested Data
Nested properties can be accessed by adding more property selectors.
let userData = { /*...the JSON above... */ };
let email = userData.contact.email; // "alice.w@example.com"
let city = userData["contact"]["address"]["city"]; // "Anystate"
Visualizing these nested structures is key, and tools like the graphical JSON viewer in Liquid Studio JSON Editor can be invaluable for navigating and understanding complex hierarchies.
Arrays of Objects
Often, you need to represent a list or collection of items, where each item is itself a complex entity with multiple attributes. This is achieved using an array of objects.
Definition and Purpose
Arrays of objects are ideal for:
- Representing lists of similar items: A list of products, a collection of user records, a series of log entries.
- Maintaining order: JSON arrays are ordered, so the sequence of objects is preserved.
Examples
JavaScript accessing Items in Arrays of Objects
Imagine a list of books, where each book has a title, author and ISBN.
You typically access an object within the array by its index (a number not a property name in the square brackets). Once you have the array item the properties can be accessed as before with either the dot or bracket notation:
let books = ...;
let firstBookTitle = books[0].title; // "The Hitchhiker's Guide to the Galaxy"
let secondBookAuthor = books[0]["author"]; // "Jane Austen"
Arrays within Objects and Objects within Arrays
The true power of JSON’s structural flexibility comes from combining these concepts. You can have arrays as values within objects, and objects as elements within arrays, to any level of depth needed.
ExampleUser object with an array of phone number objects:
Each phone number might have a type (e.g., “home”, “work”) and the number itself.
{
"userId": "user789",
"name": "Bob K.",
"phoneNumbers": [
{ "type": "home", "number": "555-0201" },
{ "type": "mobile", "number": "555-0202" }
]
}
Choosing the Right Structure: Flat vs. Nested
While JSON allows deep nesting, it’s not always the best approach. The choice between a flatter structure (more top-level keys, less nesting) and a more nested structure involves trade-offs:
Considerations:
- Data Retrieval:
- Nested: If you often need all related data together (e.g., a user and their primary address), nesting can be efficient as it might be retrieved in a single operation, especially with document databases.
- Flat: If you frequently need only specific parts of the data, or if different parts are updated independently, a flatter structure (perhaps with IDs to link to related data elsewhere) might be more suitable.
- Data Updates: Updating a deeply nested field can sometimes be more complex than updating a top-level field.
- Readability: Moderate nesting can improve readability by grouping related data. Excessive nesting can make the JSON hard to follow.
- Performance: Very large, deeply nested documents can have performance implications for parsing and transmission.
- Normalization vs. Denormalization:
- Denormalization (more nesting, data duplication): Can improve read performance by avoiding “joins” or multiple lookups, as often seen in NoSQL databases where a single document contains all necessary information for a particular view (e.g., a complete order with line items).
- Normalization (flatter, linked by IDs): Reduces data redundancy and can simplify updates, as changes are made in one place. However, it might require multiple lookups to assemble a complete view.
JSON’s flexibility allows developers to structure data in ways that best suit their application’s needs. However, this freedom requires careful thought. Without considering how data will be used and maintained, developers might create structures that become problematic later. Applying principles from database normalization, such as ensuring data integrity and minimizing redundancy, can be valuable. For example, First Normal Form (1NF) suggests that each record should be unique and contain only atomic values. While JSON doesn’t strictly enforce these, thinking about such principles can help avoid common pitfalls like duplicating data unnecessarily across multiple objects or making data dependent on only part of a key. The aim is to strike a balance that makes the data both usable for current needs and maintainable for future evolution. Liquid Technologies tools can assist in visualizing these trade-offs and designing optimal JSON structures.
Let’s look at a simple example that requires some level of normalization in order to represent it in JSON. Lets say you are trying to represent information about some teams, and the details of the players on each team.
We may try to represent this as follows, but as Bob plays for 2 teams we end up duplicating his information.
[
{
"teamName":"Tigers",
"members": [
{ "name":"Alice", "Age": 19 },
{ "name":"Bob", "Age": 25 }
]
},
{
"teamName":"Bears",
"members": [
{ "name":"Claire", "Age": 24 },
{ "name":"Bob", "Age": 25}
]
}
]
So instead we normalize this and format it as follows:
{
"teams": [
{
"teamName":"Tigers",
"members": [ "Alice", "Bob"]
},
{
"teamName":"Bears",
"members": [ "Claire", "Bob"]
}
],
"players": [
{ "name":"Alice", "Age": 19 },
{ "name":"Bob", "Age": 25 },
{ "name":"Claire", "Age": 24 },
]
}
In more complex data with recursive relationships this is the only way that the data can be represented.
Organizing Large JSON Documents
For very large JSON documents, consider these strategies:
- Logical Grouping: Use nested objects to group related sections of the data, making the overall structure more modular and understandable.
- Use of Identifiers: While JSON itself doesn’t have a formal linking mechanism like XML’s ID/IDREF or JSON Schema’s $ref for data instances, you can use unique identifiers within your data to represent relationships. Consumers of the JSON would then be responsible for resolving these links if needed.
- Avoid Unnecessary Nesting: If a piece of data doesn’t logically belong within another, keep it at a higher level or in a separate structure.
- Consider Pagination or Streaming: For extremely large datasets, especially in APIs, sending all data in one massive JSON document is often impractical. Use pagination (sending data in chunks) or streaming.
For best practices suggest using a single top-level object for most JSON documents, as this provides a clear entry point for developers take a look at JSON-LD Best Practices.
Key Collisions and Uniqueness
Duplicate Keys in Objects
The JSON specification states that “The names within an object SHOULD be unique.” This implies that while duplicate keys are discouraged, they are not strictly forbidden by the grammar. However, the behavior of parsers when encountering duplicate keys is not standardized. Most parsers will adopt a “last one wins” strategy, where the value of the last encountered key with the same name will overwrite previous ones. Relying on this behavior is risky and can lead to unpredictable results or data loss, especially if different components in a system use parsers with varying behaviors.
Best Practice: Always ensure that keys within a single JSON object are unique.
Strategies for Uniqueness
If you need to represent a collection where keys might naturally collide (e.g., using non-unique names as keys), it’s generally better to use an array of objects, where each object has a unique identifier property.
Less Ideal (potential for key collision if user_alice appears twice):
{
"alice": { "email": "alice@example.com" },
"bob": { "email": "bob@example.com" }
}
This “index-oriented” pattern, where keys represent an index, can violate normalization principles if not handled carefully, as it enforces uniqueness at the data structure level rather than through application logic.
Preferred (array of objects, each with an ID):
[
{ "id": "101", "username": "alice", "email": "alice@example.com" },
{ "id": "102", "username": "bob", "email": "bob@example.com" }
]
Some compression techniques for JSON involve mapping long keys to shorter ones, and these techniques may include options to check for collisions with existing single-character keys in the original JSON, though this can impact performance.
Summary & What’s Next
In this part, we’ve explored how to build advanced data structures in JSON using nested objects, arrays of objects, and combinations of both. We also discussed considerations for choosing appropriate structures, organizing large documents, and the importance of key uniqueness. These techniques allow JSON to effectively model a wide range of complex data scenarios. Managing and visualizing these advanced structures can be simplified with robust development tools. Liquid Technologies offer a full suite of tools that can help you model and work with complex JSON data effectively.
In Part 4: Querying and Transforming JSON Data, we will look at ways to navigate and extract specific information from these JSON structures using tools like JSON Pointer and JSONPath, as well as programmatic access.
- Part 1: Introduction to JSON
- Part 2: JSON Best Practices and Conventions
- Part 3: Advanced Data Structures in JSON (this post)
- Part 4: Querying and Transforming JSON Data


One response to “Advanced Data Structures in JSON – Part 3 of 4”
[…] practices and conventions (Part 2: Best Practices & Conventions) and advanced data structures(Part 3: Advanced Data Structures in JSON), we now turn our attention to accessing and manipulating this data. Efficiently extracting […]