JavaScript Tutorial

Working with JSON in JavaScript: Complete Guide

Master JSON in JavaScript — parsing, stringifying, nested JSON, error handling, fetch with JSON, JSON validation, transforming API responses, localStorage with JSON, and real-world examples with best practices.

Welcome back! 👋 In the previous lesson, you mastered destructuring. Now let's finish the Working with Data Structures section with one of the most practically important topics in all of web development — JSON!

JSON (JavaScript Object Notation) is the universal language of data on the web. Every API you call, every server response you receive, every config file you read — it's JSON. If you want to build anything that talks to the internet, you must understand JSON deeply.

The great news? JSON looks almost exactly like JavaScript objects, so you already know most of it. Let's master it completely!


What is JSON?

JSON is a lightweight, text-based data format for storing and exchanging data. It was designed to be easy for humans to read and easy for machines to parse.

{
  "name": "Mihir",
  "age": 25,
  "city": "Mumbai",
  "active": true,
  "scores": [92, 85, 78],
  "address": {
    "state": "Maharashtra",
    "pin": "400001"
  }
}

JSON is just a string — plain text that represents structured data. To use it as a JavaScript object, you need to parse it. To send it somewhere, you need to stringify your object.


JSON vs JavaScript Object

JSON looks like a JS object but has strict rules.

// JavaScript Object — flexible
let user = {
  name:    "Mihir",       // unquoted keys ✅
  age:     25,
  greet()  { return "Hi"; }, // methods allowed ✅
  active:  true,
  nothing: undefined,     // undefined allowed ✅
};

// JSON — strict format
let userJSON = `{
  "name":   "Mihir",     // keys MUST be double-quoted
  "age":    25,
  "active": true
                         // no methods ❌
                         // no undefined ❌
                         // no trailing commas ❌
}`;
FeatureJS ObjectJSON
Key quotesOptionalRequired (double quotes only)
String quotesSingle or doubleDouble quotes only
Methods✅ Allowed❌ Not allowed
undefined✅ Allowed❌ Not allowed (omitted)
Trailing commas✅ Allowed❌ Not allowed
Comments✅ Allowed❌ Not allowed
Data typesAll JS typesString, Number, Boolean, Array, Object, Null

JSON.stringify() — Object to JSON String

Converts a JavaScript object (or array) into a JSON string. Use this when you want to send data or store it as text.

Basic Usage

let user = {
  name:   "Mihir",
  age:    25,
  city:   "Mumbai",
  active: true,
};

let json = JSON.stringify(user);
console.log(json);
// Output: '{"name":"Mihir","age":25,"city":"Mumbai","active":true}'

console.log(typeof json); // "string"

Pretty Print with Indentation

Pass a space value (2 or 4) as the third argument for readable output.

let product = {
  name:     "Laptop",
  price:    55000,
  inStock:  true,
  specs:    { ram: "16GB", storage: "512GB" },
};

let prettyJSON = JSON.stringify(product, null, 2);
console.log(prettyJSON);
// Output:
// {
//   "name": "Laptop",
//   "price": 55000,
//   "inStock": true,
//   "specs": {
//     "ram": "16GB",
//     "storage": "512GB"
//   }
// }

Selective Stringify with Replacer Array

Pass an array of keys to include only specific fields.

let user = {
  name:     "Mihir",
  email:    "mihir@example.com",
  password: "secret123",   // sensitive!
  age:      25,
  role:     "admin",
};

// Only include safe fields
let safeJSON = JSON.stringify(user, ["name", "age", "role"]);
console.log(safeJSON);
// Output: '{"name":"Mihir","age":25,"role":"admin"}'
// password and email excluded ✅

What Gets Dropped by JSON.stringify()

let data = {
  name:      "Mihir",
  greet:     function() { return "Hi"; }, // ❌ functions — dropped
  score:     undefined,                   // ❌ undefined — dropped
  temp:      null,                        // ✅ null — kept
  active:    true,                        // ✅ boolean — kept
  tags:      ["js", "dev"],               // ✅ array — kept
};

console.log(JSON.stringify(data));
// Output: '{"name":"Mihir","temp":null,"active":true,"tags":["js","dev"]}'
// greet and score are gone!

JSON.parse() — JSON String to Object

Converts a JSON string back into a usable JavaScript object. Use this when you receive data from an API or read it from storage.

Basic Usage

let jsonString = '{"name":"Mihir","age":25,"city":"Mumbai","active":true}';

let user = JSON.parse(jsonString);

console.log(user.name);   // "Mihir"
console.log(user.age);    // 25
console.log(user.active); // true
console.log(typeof user); // "object"

Parse Nested JSON

let json = `{
  "student": {
    "name": "Priya",
    "marks": { "math": 92, "science": 88 },
    "hobbies": ["coding", "reading", "gaming"]
  }
}`;

let data = JSON.parse(json);

console.log(data.student.name);          // "Priya"
console.log(data.student.marks.math);    // 92
console.log(data.student.hobbies[0]);    // "coding"

Parse an Array of Objects

let jsonArray = `[
  { "name": "Mihir", "marks": 92 },
  { "name": "Priya", "marks": 85 },
  { "name": "Rahul", "marks": 78 }
]`;

let students = JSON.parse(jsonArray);

console.log(students.length);       // 3
console.log(students[0].name);      // "Mihir"

// Now you can use all array methods on it!
let topStudents = students.filter(s => s.marks >= 85);
console.log(topStudents.map(s => s.name)); // ["Mihir", "Priya"]

Error Handling with JSON

JSON.parse() throws a SyntaxError if the string is not valid JSON. Always wrap it in try...catch.

Without Error Handling — Dangerous

// ❌ Invalid JSON — will crash your program!
let badJSON = "{ name: 'Mihir' }"; // keys not quoted, single quotes

let data = JSON.parse(badJSON); // SyntaxError: Unexpected token n
// Program crashes! ❌

With Error Handling — Safe ✅

function safeParseJSON(jsonString) {
  try {
    return { data: JSON.parse(jsonString), error: null };
  } catch (err) {
    return { data: null, error: err.message };
  }
}

// Valid JSON
let result1 = safeParseJSON('{"name":"Mihir","age":25}');
console.log(result1.data);  // { name: "Mihir", age: 25 }
console.log(result1.error); // null

// Invalid JSON
let result2 = safeParseJSON("{ name: 'Mihir' }");
console.log(result2.data);  // null
console.log(result2.error); // "Unexpected token n in JSON at position 2"

Validate Before Parse

function isValidJSON(str) {
  try {
    JSON.parse(str);
    return true;
  } catch {
    return false;
  }
}

console.log(isValidJSON('{"name":"Mihir"}'));  // true
console.log(isValidJSON("{ name: Mihir }"));   // false
console.log(isValidJSON("[1, 2, 3]"));          // true
console.log(isValidJSON("just a string"));      // false

Deep Cloning with JSON

A quick and common way to deep clone a plain object — no nested reference issues.

let original = {
  name:    "Mihir",
  scores:  [92, 85, 78],
  address: { city: "Mumbai" },
};

// Shallow copy — nested objects still shared
let shallow = { ...original };
shallow.address.city = "Pune";
console.log(original.address.city); // "Pune" — oops! ❌

// Deep clone via JSON — fully independent
let deep = JSON.parse(JSON.stringify(original));
deep.address.city = "Delhi";
console.log(original.address.city); // "Mumbai" — safe ✅

⚠️ Limitations of JSON Deep Clone

let obj = {
  name:    "Mihir",
  greet:   function() { return "Hi"; }, // ❌ functions — lost after clone
  created: new Date(),                  // ❌ Date becomes string after clone
  nothing: undefined,                   // ❌ undefined — lost after clone
};

let clone = JSON.parse(JSON.stringify(obj));
console.log(clone.greet);    // undefined — function lost!
console.log(clone.created);  // "2024-02-13T..." — string, not Date!
console.log(clone.nothing);  // undefined — key lost!

// ✅ Only use JSON clone for plain data objects — no functions, Dates, or undefined

JSON with Fetch API

In real projects, you'll mostly encounter JSON when fetching data from APIs. Here's the complete pattern.

Reading JSON from a Fetch Response

// Standard fetch + JSON pattern
async function getUsers() {
  try {
    let response = await fetch("https://jsonplaceholder.typicode.com/users");

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    let users = await response.json(); // automatically parses JSON ✅
    console.log(`Fetched ${users.length} users`);
    return users;
  } catch (err) {
    console.error("Failed to fetch:", err.message);
    return [];
  }
}

Sending JSON with POST

async function createUser(userData) {
  try {
    let response = await fetch("https://api.example.com/users", {
      method:  "POST",
      headers: {
        "Content-Type": "application/json", // tell server we're sending JSON
      },
      body: JSON.stringify(userData), // convert object to JSON string
    });

    let result = await response.json();
    console.log("Created:", result);
    return result;
  } catch (err) {
    console.error("Failed to create user:", err.message);
  }
}

createUser({ name: "Mihir", email: "mihir@example.com", role: "admin" });

Simulating a Full API Workflow (Without Network)

// Simulate: API response as a raw JSON string
let rawResponse = `{
  "status": 200,
  "message": "OK",
  "data": {
    "users": [
      { "id": 1, "name": "Mihir",  "role": "admin",  "active": true  },
      { "id": 2, "name": "Priya",  "role": "editor", "active": true  },
      { "id": 3, "name": "Rahul",  "role": "viewer", "active": false }
    ],
    "total": 3,
    "page": 1
  }
}`;

// Step 1: Parse the JSON
let response = JSON.parse(rawResponse);

// Step 2: Destructure what you need
let { status, data: { users, total } } = response;

// Step 3: Process
let activeUsers = users.filter(u => u.active);

console.log(`Status: ${status}`);
console.log(`Total Users: ${total}`);
console.log(`Active: ${activeUsers.length}`);
activeUsers.forEach(u => console.log(`  [${u.role}] ${u.name}`));
// Output:
// Status: 200
// Total Users: 3
// Active: 2
//   [admin] Mihir
//   [editor] Priya

JSON with localStorage

Browsers only store strings in localStorage — so use JSON to save and load objects.

// Save object to localStorage
function saveToStorage(key, value) {
  try {
    localStorage.setItem(key, JSON.stringify(value));
    console.log(`✅ Saved "${key}" to storage`);
  } catch (err) {
    console.error("Failed to save:", err.message);
  }
}

// Load object from localStorage
function loadFromStorage(key, defaultValue = null) {
  try {
    let raw = localStorage.getItem(key);
    return raw ? JSON.parse(raw) : defaultValue;
  } catch (err) {
    console.error("Failed to load:", err.message);
    return defaultValue;
  }
}

// Usage
let userPrefs = { theme: "dark", fontSize: 16, language: "en" };
saveToStorage("userPrefs", userPrefs);

let loaded = loadFromStorage("userPrefs", { theme: "light" });
console.log(loaded.theme); // "dark"

// List not found — returns default
let missing = loadFromStorage("notExist", []);
console.log(missing); // []

Transforming JSON Data

Real-world API JSON often needs transformation before use.

Rename Keys (snake_case → camelCase)

let apiData = [
  { user_id: 1, first_name: "Mihir",  last_name: "Shah",  is_active: true  },
  { user_id: 2, first_name: "Priya",  last_name: "Nair",  is_active: false },
  { user_id: 3, first_name: "Rahul",  last_name: "Gupta", is_active: true  },
];

let cleanData = apiData.map(({
  user_id:    id,
  first_name: firstName,
  last_name:  lastName,
  is_active:  isActive,
}) => ({
  id,
  name:     `${firstName} ${lastName}`,
  isActive,
  initials: `${firstName[0]}${lastName[0]}`.toUpperCase(),
}));

console.log(JSON.stringify(cleanData, null, 2));
// [
//   { "id": 1, "name": "Mihir Shah",  "isActive": true,  "initials": "MS" },
//   { "id": 2, "name": "Priya Nair",  "isActive": false, "initials": "PN" },
//   { "id": 3, "name": "Rahul Gupta", "isActive": true,  "initials": "RG" }
// ]

Filter and Reshape

let rawProducts = `[
  { "id": 1, "title": "Laptop",  "price": 55000, "category": "electronics", "stock": 5  },
  { "id": 2, "title": "T-Shirt", "price": 799,   "category": "clothing",    "stock": 0  },
  { "id": 3, "title": "Phone",   "price": 18000, "category": "electronics", "stock": 12 },
  { "id": 4, "title": "Jeans",   "price": 1499,  "category": "clothing",    "stock": 8  }
]`;

let products  = JSON.parse(rawProducts);

let catalogue = products
  .filter(p => p.stock > 0)
  .map(({ id, title, price, category }) => ({
    id,
    name:     title,
    price:    `₹${price.toLocaleString()}`,
    category: category.charAt(0).toUpperCase() + category.slice(1),
    inStock:  true,
  }));

console.log(JSON.stringify(catalogue, null, 2));
// [
//   { "id": 1, "name": "Laptop", "price": "₹55,000", "category": "Electronics", "inStock": true },
//   { "id": 3, "name": "Phone",  "price": "₹18,000", "category": "Electronics", "inStock": true },
//   { "id": 4, "name": "Jeans",  "price": "₹1,499",  "category": "Clothing",    "inStock": true }
// ]

Real-World Examples

Example 1: Config File Manager

// Simulated config.json content
let configJSON = `{
  "app": {
    "name": "LearnJS",
    "version": "2.1.0",
    "environment": "production"
  },
  "database": {
    "host": "db.learnjs.com",
    "port": 5432,
    "name": "learnjs_prod"
  },
  "features": {
    "darkMode":      true,
    "notifications": true,
    "analytics":     false
  },
  "limits": {
    "maxUploadMB": 10,
    "sessionMinutes": 60,
    "maxLoginAttempts": 3
  }
}`;

let config = JSON.parse(configJSON);

// Destructure what you need
let { app, features, limits } = config;

console.log(`🚀 ${app.name} v${app.version} [${app.environment}]`);
console.log(`\n📌 Features:`);
for (let [feature, enabled] of Object.entries(features)) {
  console.log(`   ${feature.padEnd(18)}: ${enabled ? "✅ On" : "❌ Off"}`);
}
console.log(`\n⚙️  Limits:`);
for (let [limit, value] of Object.entries(limits)) {
  console.log(`   ${limit.padEnd(20)}: ${value}`);
}
// Output:
// 🚀 LearnJS v2.1.0 [production]
// 📌 Features:
//    darkMode          : ✅ On
//    notifications     : ✅ On
//    analytics         : ❌ Off
// ⚙️  Limits:
//    maxUploadMB       : 10
//    sessionMinutes    : 60
//    maxLoginAttempts  : 3

Example 2: Student Data Pipeline

// Raw JSON from a database
let rawJSON = `{
  "semester": "Spring 2024",
  "students": [
    { "id": "S001", "name": "Mihir",  "scores": { "math": 92, "science": 88, "english": 85 } },
    { "id": "S002", "name": "Priya",  "scores": { "math": 78, "science": 91, "english": 82 } },
    { "id": "S003", "name": "Rahul",  "scores": { "math": 65, "science": 70, "english": 60 } },
    { "id": "S004", "name": "Sara",   "scores": { "math": 95, "science": 93, "english": 90 } },
    { "id": "S005", "name": "Arjun",  "scores": { "math": 55, "science": 60, "english": 50 } }
  ]
}`;

let { semester, students } = JSON.parse(rawJSON);

// Process
let report = students.map(({ id, name, scores }) => {
  let values  = Object.values(scores);
  let average = (values.reduce((s, n) => s + n, 0) / values.length).toFixed(1);
  let grade   = average >= 90 ? "A" : average >= 75 ? "B" : average >= 60 ? "C" : "F";
  let status  = average >= 60 ? "Pass ✅" : "Fail ❌";
  return { id, name, average: parseFloat(average), grade, status };
});

let classAvg = (
  report.reduce((s, s2) => s + s2.average, 0) / report.length
).toFixed(1);

// Display
console.log(`\n📚 ${semester} — Student Report`);
console.log("=".repeat(55));
report
  .sort((a, b) => b.average - a.average)
  .forEach(({ id, name, average, grade, status }, i) => {
    console.log(`${i + 1}. [${id}] ${name.padEnd(8)} Avg: ${average} | ${grade} | ${status}`);
  });
console.log("=".repeat(55));
console.log(`Class Average: ${classAvg}`);

// Serialize back to JSON for storage/API
let outputJSON = JSON.stringify({ semester, report, classAvg }, null, 2);
console.log("\n📤 Output JSON (first 200 chars):");
console.log(outputJSON.slice(0, 200) + "...");

Example 3: Shopping Cart with Persistence

// Simulate browser localStorage with a plain object
let fakeStorage = {};

const storage = {
  getItem: key => fakeStorage[key] ?? null,
  setItem: (key, val) => { fakeStorage[key] = val; },
  removeItem: key => { delete fakeStorage[key]; },
};

// Cart manager with JSON persistence
let cartManager = {
  STORAGE_KEY: "shopping_cart",

  load() {
    try {
      let raw = storage.getItem(this.STORAGE_KEY);
      return raw ? JSON.parse(raw) : [];
    } catch {
      return [];
    }
  },

  save(cart) {
    storage.setItem(this.STORAGE_KEY, JSON.stringify(cart));
  },

  add(item) {
    let cart = this.load();
    let existing = cart.find(i => i.id === item.id);

    if (existing) {
      existing.qty += 1;
      console.log(`🔄 Updated qty: ${item.name} (${existing.qty})`);
    } else {
      cart.push({ ...item, qty: 1 });
      console.log(`✅ Added: ${item.name}`);
    }

    this.save(cart);
  },

  remove(id) {
    let cart = this.load().filter(i => i.id !== id);
    this.save(cart);
    console.log(`🗑️  Removed item #${id}`);
  },

  summary() {
    let cart  = this.load();
    let total = cart.reduce((s, i) => s + i.price * i.qty, 0);

    console.log("\n🛒 Cart:");
    cart.forEach(({ name, price, qty }) => {
      console.log(`   ${name.padEnd(14)}${price} × ${qty} = ₹${price * qty}`);
    });
    console.log(`   ${"─".repeat(38)}`);
    console.log(`   TOTAL: ₹${total.toLocaleString()}`);
  },
};

cartManager.add({ id: 1, name: "Laptop",   price: 55000 });
cartManager.add({ id: 2, name: "Mouse",    price: 800   });
cartManager.add({ id: 3, name: "Keyboard", price: 1500  });
cartManager.add({ id: 1, name: "Laptop",   price: 55000 }); // qty++
cartManager.remove(2);
cartManager.summary();
// Output:
// ✅ Added: Laptop
// ✅ Added: Mouse
// ✅ Added: Keyboard
// 🔄 Updated qty: Laptop (2)
// 🗑️  Removed item #2
// 🛒 Cart:
//    Laptop         ₹55000 × 2 = ₹110000
//    Keyboard       ₹1500 × 1  = ₹1500
//    ──────────────────────────────────────
//    TOTAL: ₹1,11,500

Common Mistakes

Mistake 1: Parsing Already-Parsed Data

let user = { name: "Mihir", age: 25 }; // Already a JS object

// ❌ Don't parse an object — it's not a JSON string!
let parsed = JSON.parse(user);
// Returns "[object Object]" as parsed — wrong!

// ✅ Only parse strings
let jsonString = '{"name":"Mihir","age":25}';
let parsed = JSON.parse(jsonString); // ✅

Mistake 2: Invalid JSON Syntax

// These are NOT valid JSON:

// ❌ Single quotes
JSON.parse("{'name': 'Mihir'}"); // SyntaxError

// ❌ Unquoted keys
JSON.parse("{name: 'Mihir'}"); // SyntaxError

// ❌ Trailing comma
JSON.parse('{"name":"Mihir","age":25,}'); // SyntaxError

// ❌ Comments
JSON.parse('{"name":"Mihir" // user name }'); // SyntaxError

// ✅ Valid JSON only
JSON.parse('{"name":"Mihir","age":25}'); // ✅

Mistake 3: Forgetting try...catch Around JSON.parse()

// ❌ No error handling — one bad string crashes everything
function processData(jsonStr) {
  let data = JSON.parse(jsonStr); // could throw!
  return data.users;
}

// ✅ Always wrap JSON.parse in try...catch
function processData(jsonStr) {
  try {
    let data = JSON.parse(jsonStr);
    return data.users ?? [];
  } catch (err) {
    console.error("Invalid JSON:", err.message);
    return [];
  }
}

Mistake 4: Expecting Functions and undefined to Survive Stringify

let user = {
  name:    "Mihir",
  greet:   () => "Hello",   // function
  score:   undefined,        // undefined
  active:  true,
};

let json = JSON.stringify(user);
console.log(json);
// '{"name":"Mihir","active":true}'
// greet and score are silently GONE! ❌

// ✅ Be explicit — if a value might be undefined, set it to null
let safeUser = {
  name:   "Mihir",
  score:  null,   // null survives stringify
  active: true,
};
JSON.stringify(safeUser);
// '{"name":"Mihir","score":null,"active":true}' ✅

Mistake 5: Mutating a Parsed Object and Expecting JSON to Update

let jsonStr = '{"name":"Mihir","count":0}';
let obj = JSON.parse(jsonStr);

obj.count = 99; // ✅ modifies the JS object

// ❌ jsonStr is still the original string — it doesn't auto-update!
console.log(jsonStr); // '{"name":"Mihir","count":0}' — unchanged

// ✅ Re-stringify to get updated JSON
let updatedJSON = JSON.stringify(obj);
console.log(updatedJSON); // '{"name":"Mihir","count":99}' ✅

Practical Exercise

Create a file called working-with-json.js:

// 🎯 Working with JSON Practice

// 1. Parse and process a product catalog
console.log("=== 🛍️ Product Catalog ===");
let catalogJSON = `{
  "store": "TechMart",
  "lastUpdated": "2024-02-13",
  "products": [
    { "id": 1, "name": "Laptop",    "price": 55000, "category": "Electronics", "stock": 5,  "rating": 4.5 },
    { "id": 2, "name": "T-Shirt",   "price": 799,   "category": "Clothing",    "stock": 20, "rating": 4.1 },
    { "id": 3, "name": "Phone",     "price": 18000, "category": "Electronics", "stock": 0,  "rating": 4.8 },
    { "id": 4, "name": "Sneakers",  "price": 3999,  "category": "Footwear",    "stock": 8,  "rating": 4.3 },
    { "id": 5, "name": "Earbuds",   "price": 2500,  "category": "Electronics", "stock": 15, "rating": 4.6 }
  ]
}`;

let { store, products } = JSON.parse(catalogJSON);

let inStock      = products.filter(p => p.stock > 0);
let electronics  = inStock.filter(p => p.category === "Electronics");
let totalValue   = products.reduce((s, p) => s + p.price * p.stock, 0);
let topRated     = [...products].sort((a, b) => b.rating - a.rating)[0];

console.log(`Store: ${store}`);
console.log(`In Stock: ${inStock.length}/${products.length} products`);
console.log(`Electronics available: ${electronics.map(p => p.name).join(", ")}`);
console.log(`Total Inventory Value: ₹${totalValue.toLocaleString()}`);
console.log(`Top Rated: ${topRated.name} (⭐${topRated.rating})`);


// 2. Safe JSON parser with validation
console.log("\n=== 🛡️ Safe JSON Parser ===");
function safeParseJSON(str, fallback = null) {
  try {
    let parsed = JSON.parse(str);
    return { success: true, data: parsed, error: null };
  } catch (err) {
    return { success: false, data: fallback, error: err.message };
  }
}

let tests = [
  '{"name":"Mihir","age":25}',
  "[1, 2, 3, 4, 5]",
  "{ name: Mihir }",           // invalid
  '{"active":true,"score":null}',
  "not json at all",           // invalid
];

tests.forEach(test => {
  let { success, data, error } = safeParseJSON(test);
  console.log(success
    ? `✅ Valid  | ${JSON.stringify(data)}`
    : `❌ Invalid| ${error.split(" ").slice(0, 5).join(" ")}...`
  );
});


// 3. Build, save, and reload a gradebook
console.log("\n=== 📚 Gradebook Persistence ===");
let fakeStorage = {};

function saveData(key, data) {
  fakeStorage[key] = JSON.stringify(data);
  console.log(`💾 Saved "${key}"`);
}

function loadData(key, fallback = null) {
  let raw = fakeStorage[key];
  return raw ? JSON.parse(raw) : fallback;
}

let gradebook = {
  class: "JavaScript 101",
  term:  "Spring 2024",
  students: [
    { name: "Mihir", scores: [92, 88, 95, 90] },
    { name: "Priya", scores: [78, 82, 85, 80] },
    { name: "Rahul", scores: [60, 55, 65, 70] },
  ],
};

// Add computed fields before saving
gradebook.students = gradebook.students.map(s => {
  let avg   = (s.scores.reduce((a, b) => a + b, 0) / s.scores.length).toFixed(1);
  let grade = avg >= 90 ? "A" : avg >= 75 ? "B" : avg >= 60 ? "C" : "F";
  return { ...s, average: parseFloat(avg), grade };
});

saveData("gradebook", gradebook);

// Reload and display
let loaded = loadData("gradebook");
console.log(`\nLoaded: ${loaded.class}${loaded.term}`);
loaded.students
  .sort((a, b) => b.average - a.average)
  .forEach(({ name, average, grade }, i) => {
    console.log(`  ${i + 1}. ${name.padEnd(8)} Avg: ${average} | Grade: ${grade}`);
  });

Run it:

node working-with-json.js

Expected Output:

=== 🛍️ Product Catalog ===
Store: TechMart
In Stock: 4/5 products
Electronics available: Laptop, Earbuds
Total Inventory Value: ₹3,44,480
Top Rated: Phone (⭐4.8)

=== 🛡️ Safe JSON Parser ===
✅ Valid  | {"name":"Mihir","age":25}
✅ Valid  | [1,2,3,4,5]
❌ Invalid| Unexpected token n in JSON...
✅ Valid  | {"active":true,"score":null}
❌ Invalid| Unexpected token o in JSON...

=== 📚 Gradebook Persistence ===
💾 Saved "gradebook"

Loaded: JavaScript 101Spring 2024
  1. Mihir    Avg: 91.3 | Grade: A
  2. Priya    Avg: 81.3 | Grade: B
  3. Rahul    Avg: 62.5 | Grade: C

Key Takeaways

Congratulations! 🎉 You now fully understand how to work with JSON in JavaScript.

JSON is a text-based data format — a string, not an object. It's the universal language of data exchange on the web.

JSON.stringify(obj) — converts a JS object to a JSON string. Functions and undefined are dropped silently.

JSON.parse(str) — converts a JSON string to a JS object. Always wrap in try...catch.

Pretty printJSON.stringify(obj, null, 2) for readable, indented output.

Selective stringify — pass an array of keys as the second argument to include only specific fields.

Error handling — invalid JSON throws SyntaxError. Always guard with try...catch in production.

Deep cloneJSON.parse(JSON.stringify(obj)) for plain objects. Loses functions, Dates, and undefined.

With Fetchresponse.json() automatically parses. JSON.stringify(data) for POST body.

With localStorage — always stringify before saving, parse after loading.


Best Practices

  1. Always use try...catch around JSON.parse() — never assume the input is valid
  2. ✅ Use JSON.stringify(data, null, 2) when logging or debugging — much easier to read
  3. ✅ Use the replacer array to exclude sensitive fields before sending JSON
  4. ✅ Use null instead of undefined for missing values — undefined gets dropped by stringify
  5. ✅ Only use JSON deep clone for plain data objects — not for objects with methods or Dates
  6. ✅ Always set Content-Type: application/json header when sending JSON to an API
  7. Validate API responses before destructuring — don't assume the shape is always correct
  8. ✅ Build a safeParseJSON utility function in your projects — use it everywhere instead of raw JSON.parse

🎉 Working with Data Structures — Section Complete!

Amazing work! You've now completed the entire Working with Data Structures section! Here's everything you've mastered:

TopicWhat You Learned
StringsMethods, search, slice, replace, split/join
Template LiteralsInterpolation, multi-line, tagged templates
ArraysCRUD, map/filter/reduce, sort, spread
Loops with ArraysPatterns, for...of, forEach, break/continue
Map, Filter, ReduceTransform, filter, accumulate, chaining
ObjectsKeys/values, methods, this, Object.* helpers
DestructuringArrays, objects, nested, params, defaults
JSONParse, stringify, fetch, error handling