Mastering JSON in JavaScript 🧩
BrainyTools Editor
Tech Contributor at BrainyTools

Mastering JSON in JavaScript: Formatting, Transformation, Merging, Extraction, and Recursive Manipulation
In modern software development and data analysis, JSON (JavaScript Object Notation) has become the universal language for data exchange. Whether you’re building APIs, integrating third-party services, storing configuration data, or processing analytics pipelines, JSON is everywhere.
But while most developers are comfortable with JSON.parse() and JSON.stringify(), far fewer truly master JSON transformation and manipulation, especially when it comes to:
- Merging complex objects
- Extracting deeply nested data
- Recursively modifying structures
- Cleaning and formatting JSON for real-world systems
This article is a deep dive into JSON manipulation in JavaScript, covering both built-in capabilities and popular libraries, with a strong focus on practical, real-world use cases.
Why JSON Mastery Matters
Let’s start with a simple example:
{
"user": {
"name": "John Doe",
"email": "john@example.com",
"preferences": {
"theme": "dark",
"notifications": true
}
}
}
Looks simple, right?
Now imagine:
- You need to merge this with another object
- Extract only specific fields
- Remove sensitive keys like
email - Flatten it for analytics
- Convert it into another schema
Suddenly, JSON becomes more than just a format. It becomes a data transformation problem.
Understanding JSON vs JavaScript Objects
Before going deeper, clarify this:
JSON is a string format, while JavaScript objects are in-memory structures.
Conversion
const jsonString = '{"name": "John"}';
const obj = JSON.parse(jsonString);
const backToString = JSON.stringify(obj);
1. JSON Formatting and Pretty Printing
Basic Stringify
const obj = { name: "John", age: 30 };
console.log(JSON.stringify(obj));
// {"name":"John","age":30}
Pretty Printing
console.log(JSON.stringify(obj, null, 2));
Output
{
"name": "John",
"age": 30
}
Custom Formatting with Replacer
const obj = {
name: "John",
password: "secret"
};
const filtered = JSON.stringify(obj, (key, value) => {
if (key === "password") return undefined;
return value;
}, 2);
console.log(filtered);
Output
{
"name": "John"
}
2. Merging JSON Objects
Merging is one of the most common operations.
2.1 Using Spread Operator
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = { ...obj1, ...obj2 };
console.log(merged);
// { a: 1, b: 3, c: 4 }
2.2 Using Object.assign()
const merged = Object.assign({}, obj1, obj2);
Limitation: Shallow Merge
const obj1 = { user: { name: "John" } };
const obj2 = { user: { age: 30 } };
const merged = { ...obj1, ...obj2 };
console.log(merged);
// { user: { age: 30 } }
Data is lost.
2.3 Deep Merge (Custom)
function deepMerge(target, source) {
for (let key in source) {
if (
source[key] instanceof Object &&
key in target
) {
Object.assign(source[key], deepMerge(target[key], source[key]));
}
}
return { ...target, ...source };
}
Example
const obj1 = { user: { name: "John" } };
const obj2 = { user: { age: 30 } };
console.log(deepMerge(obj1, obj2));
// { user: { name: "John", age: 30 } }
2.4 Using Lodash
import _ from "lodash";
const merged = _.merge(obj1, obj2);
3. Extracting Data from JSON
3.1 Basic Access
const data = {
user: {
profile: {
name: "John"
}
}
};
console.log(data.user.profile.name);
3.2 Optional Chaining
console.log(data?.user?.profile?.name);
Prevents runtime errors.
3.3 Dynamic Path Extraction
function getValue(obj, path) {
return path.split(".").reduce((o, key) => o?.[key], obj);
}
console.log(getValue(data, "user.profile.name"));
// John
3.4 Using Lodash get()
_.get(data, "user.profile.name");
4. Recursive JSON Manipulation
This is where things get powerful.
4.1 Removing Keys Recursively
Problem
Remove all "password" keys from a deeply nested object.
Solution
function removeKey(obj, keyToRemove) {
if (Array.isArray(obj)) {
return obj.map(item => removeKey(item, keyToRemove));
} else if (obj !== null && typeof obj === "object") {
return Object.keys(obj).reduce((acc, key) => {
if (key !== keyToRemove) {
acc[key] = removeKey(obj[key], keyToRemove);
}
return acc;
}, {});
}
return obj;
}
Example
const data = {
user: {
name: "John",
password: "1234"
}
};
console.log(removeKey(data, "password"));
4.2 Flattening JSON
function flatten(obj, parent = "", res = {}) {
for (let key in obj) {
const propName = parent ? `${parent}.${key}` : key;
if (typeof obj[key] === "object") {
flatten(obj[key], propName, res);
} else {
res[propName] = obj[key];
}
}
return res;
}
Output
{
"user.name": "John"
}
4.3 Deep Key Renaming
function renameKey(obj, oldKey, newKey) {
if (Array.isArray(obj)) {
return obj.map(item => renameKey(item, oldKey, newKey));
} else if (typeof obj === "object" && obj !== null) {
const newObj = {};
for (let key in obj) {
const updatedKey = key === oldKey ? newKey : key;
newObj[updatedKey] = renameKey(obj[key], oldKey, newKey);
}
return newObj;
}
return obj;
}
5. Filtering JSON Data
const users = [
{ name: "John", age: 30 },
{ name: "Jane", age: 25 }
];
const filtered = users.filter(u => u.age > 26);
Deep Filtering
function filterKeys(obj, allowedKeys) {
return Object.keys(obj)
.filter(key => allowedKeys.includes(key))
.reduce((res, key) => {
res[key] = obj[key];
return res;
}, {});
}
6. JSON Transformation Pipelines
In data-heavy applications, transformations are chained.
Example Pipeline
const result = users
.map(u => ({ ...u, ageGroup: u.age > 26 ? "Adult" : "Young" }))
.filter(u => u.age > 20);
7. JSON Schema Transformation
Convert structure:
const input = {
firstName: "John",
lastName: "Doe"
};
const output = {
fullName: `${input.firstName} ${input.lastName}`
};
8. Popular Libraries
8.1 Lodash
_.merge(obj1, obj2);
_.get(obj, "path.to.value");
_.omit(obj, ["password"]);
8.2 Ramda (Functional)
R.path(["user", "name"], data);
8.3 Immer
import produce from "immer";
const nextState = produce(state, draft => {
draft.user.name = "Jane";
});
8.4 JSONPath
jsonpath.query(data, "$.user.name");
9. Real-World Applications
9.1 API Data Cleaning
const cleaned = removeKey(apiResponse, "internalId");
9.2 Analytics Flattening
const flat = flatten(eventData);
9.3 Configuration Merging
const config = deepMerge(defaultConfig, userConfig);
9.4 Security Sanitization
Remove sensitive fields:
removeKey(data, "password");
removeKey(data, "token");
10. Common Pitfalls
Circular References
JSON.stringify(obj); // crashes if circular
Data Loss
- Functions
- Undefined
- Symbols
Performance Issues
Deep recursion can be expensive.
11. Best Practices
- Always validate JSON before parsing
- Use deep merge carefully
- Prefer immutability
- Sanitize sensitive data
- Use libraries when complexity grows
Final Thoughts
JSON is more than a data format. It is the backbone of modern software systems.
From APIs to analytics pipelines, mastering JSON transformation gives you the ability to:
- Shape data intelligently
- Build scalable systems
- Prevent subtle bugs
- Improve performance and clarity
The key insight is this:
Data is rarely in the format you need. It’s your job to transform it.
And the better you become at JSON manipulation, the more powerful and efficient your systems will be.