5 Secret features of JSON in JavaScript you didn't know about 🤯
Siddharth

Siddharth @siddharthshyniben

About: Making wild out-on-a-limb schemes which can change the world if they work, but probably wont.

Location:
Kannur, Kerala, India
Joined:
Apr 1, 2021

5 Secret features of JSON in JavaScript you didn't know about 🤯

Publish Date: Mar 1 '22
601 26

I'm pretty sure you have used the global JSON object for a variety of things, like in fetch requests and to avoid the dreaded [object Object]. I also bet you didn't know about the rest of the largely unknown features that JSON can provide!

JSON can do cool stuff like revive data, use a custom format to encode/decode data, hide certain properties in stringified data, and format your JSON! 🤯

Sound interesting? Let's dive into it!

1. Formatting

The default stringifier also minifies the JSON, which looks ugly

const user = {
  name: 'John',
  age: 30,
  isAdmin: true,
  friends: ['Bob', 'Jane'],
  address: {
    city: 'New York',
    country: 'USA'
  }
};

console.log(JSON.stringify(user));
//=> {"name":"John","age":30,"isAdmin":true,"friends":["Bob","Jane"],"address":{"city":"New York","country":"USA"}}
Enter fullscreen mode Exit fullscreen mode

JSON.stringify has a built in formatter too!

console.log(JSON.stringify(user, null, 2));
// {
//   "name": "John",
//   "age": 30,
//   "isAdmin": true,
//   "friends": [
//     "Bob",
//     "Jane"
//   ],
//   "address": {
//     "city": "New York",
//     "country": "USA"
//   }
// }
Enter fullscreen mode Exit fullscreen mode

(If you are wondering what that null is, we'll come to it later)

In this example, the JSON was formatted with 2 spaces of indentation.

We can also specify a custom character to use for indentation.

console.log(JSON.stringify(user, null, 'lol'));
// {
// lol"name": "John",
// lol"age": 30,
// lol"isAdmin": true,
// lol"friends": [
// lollol"Bob",
// lollol"Jane"
// lol],
// lol"address": {
// lollol"city": "New York",
// lollol"country": "USA"
// lol}
// }
Enter fullscreen mode Exit fullscreen mode

2. Hiding certain properties in stringified data

JSON.stringify had a second argument which is largely unknown. It's called the replacer and it's a function or array that decides which data to keep in the output and which not to.

Here's a simple example where we can hide the password of a user.

const user = {
  name: 'John',
  password: '12345',
  age: 30
};

console.log(JSON.stringify(user, (key, value) => {
    if (key === 'password') {
            return;
    }

    return value;
}));
Enter fullscreen mode Exit fullscreen mode

And this is the output:

{"name":"John","age":30}
Enter fullscreen mode Exit fullscreen mode

We can further refactor this:

function stripKeys(...keys) {
    return (key, value) => {
        if (keys.includes(key)) {
            return;
        }

        return value;
    };
}

const user = {
  name: 'John',
  password: '12345',
  age: 30,
  gender: 'male'
};

console.log(JSON.stringify(user, stripKeys('password', 'gender')))
Enter fullscreen mode Exit fullscreen mode

Which outputs:

{"name":"John","age":30}
Enter fullscreen mode Exit fullscreen mode

You can also pass an array to get certain keys only:

const user = {
    name: 'John',
    password: '12345',
    age: 30
}

console.log(JSON.stringify(user, ['name', 'age']))
Enter fullscreen mode Exit fullscreen mode

Which output the same thing.

The cool thing is this works on arrays too. If you had a huge array of cakes:

const cakes = [
    {
        name: 'Chocolate Cake',
        recipe: [
            'Mix flour, sugar, cocoa powder, baking powder, eggs, vanilla, and butter',
            'Mix in milk',
            'Bake at 350 degrees for 1 hour',
            // ...
        ],
        ingredients: ['flour', 'sugar', 'cocoa powder', 'baking powder', 'eggs', 'vanilla', 'butter']
    },
    // tons of these
];
Enter fullscreen mode Exit fullscreen mode

We can easily do the same thing, and the replacer will be applied to each cake:

const cakes = [
    {
        name: 'Chocolate Cake',
        recipe: [
            'Mix flour, sugar, cocoa powder, baking powder, eggs, vanilla, and butter',
            'Mix in milk',
            'Bake at 350 degrees for 1 hour',
            // ...
        ],
        ingredients: ['flour', 'sugar', 'cocoa powder', 'baking powder', 'eggs', 'vanilla', 'butter']
    },
    // tons of these
];

console.log(JSON.stringify(cakes, ['name']))
Enter fullscreen mode Exit fullscreen mode

We get this:

[{"name":"Chocolate Cake"},{"name":"Vanilla Cake"},...]
Enter fullscreen mode Exit fullscreen mode

Cool stuff!

3. Using toJSON to create custom output formats

If an object implements the toJSON function, JSON.stringify will use it to stringify the data.

Consider this:

class Fraction {
  constructor(n, d) {
    this.numerator = n;
    this.denominator = d;
  }
}

console.log(JSON.stringify(new Fraction(1, 2)))
Enter fullscreen mode Exit fullscreen mode

This would output {"numerator":1,"denominator":2}. But what if we wanted to replace this with a string 1/2?

Enter toJSON

class Fraction {
  constructor(n, d) {
    this.numerator = n;
    this.denominator = d;
  }

  toJSON() {
      return `${this.numerator}/${this.denominator}`
  }
}

console.log(JSON.stringify(new Fraction(1, 2)))
Enter fullscreen mode Exit fullscreen mode

JSON.stringify respects the toJSON property and output "1/2".

4. Reviving data

Our fraction example above works nicely. But what if we want to revive the data? Wouldn't it be cool if the fraction would be magically brought back when we parse the JSON again? We can!

Enter revivers!

class Fraction {
  constructor(n, d) {
    this.numerator = n;
    this.denominator = d;
  }

  toJSON() {
      return `${this.numerator}/${this.denominator}`
  }

  static fromJSON(key, value) {
    if (typeof value === 'string') {
        const parts = value.split('/').map(Number);
        if (parts.length === 2) return new Fraction(parts);
    }

    return value;
  }
}

const fraction = new Fraction(1, 2);
const stringified = JSON.stringify(fraction);
console.log(stringified);
// "1/2"
const revived = JSON.parse(stringified, Fraction.fromJSON);
console.log(revived);
// Fraction { numerator: 1, denominator: 2 }
Enter fullscreen mode Exit fullscreen mode

We can pass a second argument to JSON.parse to specify a reviver function. The job of the reviver is to "revive" stringified data back into it's original form. Here, we are passing a reviver, which is the static proprty fromJSON of the Fraction class.

In this case the reviver checks if the value is a valid fraction and if it is, it creates a new Fraction object and returns it.

Fun fact: this feature is used in the built-in Date object. Try looking up Date.prototype.toJSON
That's why this works:

console.log(JSON.stringify(new Date()))
//=> '"2022-03-01T06:28:41.308Z"'

To revive the date, we can use JSON.parse:

function reviveDate(key, value) {
    const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,}|)Z$/;

    if (typeof value === "string" && regex.test(value)) {
        return new Date(value);
    }

    return value;
}
console.log(JSON.parse('"2022-03-01T06:28:41.308Z"', reviveDate))
//=> Tue Mar 01 2022 06:28:41 GMT-0700 (Pacific Daylight Time)

5. Using revivers to hide data

Like resolvers, revivers can also be used to hide data. It works in the same way.

Here's an example:

const user = JSON.stringify({
  name: 'John',
  password: '12345',
  age: 30
});

console.log(JSON.parse(user, (key, value) => {
    if (key === 'password') {
            return;
    }

    return value;
}));
Enter fullscreen mode Exit fullscreen mode

And this is the output:

{ name: 'John', age: 30 }
Enter fullscreen mode Exit fullscreen mode

As an exercise, check if you can rewrite the previously shown resolvers as revivers.

That's a wrap!

Let me know if you know any other cool JSON tricks 👀

Thanks for reading!

Comments 26 total

  • SunflowerToadTheOrbiter
    SunflowerToadTheOrbiterMar 1, 2022

    Really cool, thanks for sharing!

    • Siddharth
      SiddharthMar 2, 2022

      Thanks for reading, too!

  • Lucia Cerchie
    Lucia CerchieMar 1, 2022

    🤯 Thanks for writing this, I learned a lot! What would a reviver for an array like the cakes array look like? Would you filter the array and populate the rest of the details if the name matched?

    • Siddharth
      SiddharthMar 2, 2022

      It would probably be impossible to write a reviver for the current reducer I wrote, since all data is deleted. If we did have a reference to the original array, yes we could find the matching cake and then populate the rest of the data.

  • Vatsal
    Vatsal Mar 1, 2022

    Awesome 😎

  • ToxicToast
    ToxicToastMar 1, 2022

    Please edit the Title.
    This has nothing todo with JSON directly it's more features of Javascript you're showing...

    • Pratik sharma
      Pratik sharmaMar 2, 2022

      +1

    • Siddharth
      SiddharthMar 2, 2022

      I meant the JSON object in JavaScript, yes. I'll edit it. Thanks!

    • Hélio oliveira
      Hélio oliveiraMar 2, 2022

      Agreed. Looks like clickbait.

    • JoelBonetR 🥇
      JoelBonetR 🥇Mar 3, 2022

      Well JSON states for JavaScript Object Notation so I'm giving a point to the OP here 😂

      • Siddharth
        SiddharthMar 3, 2022

        I never really thought about that 😂

      • ToxicToast
        ToxicToastMar 3, 2022

        Oh a "Knows it all" if you want to smartass, then you probably know that JSON is still not the same as Javascript.

        • JoelBonetR 🥇
          JoelBonetR 🥇Mar 4, 2022

          Of course not, I just defined what JSON states for. Please sir, continue scrolling, nothing to see here

  • Adam Crockett 🌀
    Adam Crockett 🌀Mar 1, 2022

    👏 bravo I knew 1 and a half of these, learn something new about old dogs every day... Is that the expression

  • SeongKuk Han
    SeongKuk HanMar 2, 2022

    Awesome! Thanks for sharing

  • Siddharth
    SiddharthMar 2, 2022

    Thanks! :D

  • Elijah Trillionz
    Elijah TrillionzMar 2, 2022

    Great writeup.
    Thanks for sharing

  • Shubh4nk
    Shubh4nkMar 3, 2022

    Awesome

  • Pankaj Sharma
    Pankaj SharmaMar 3, 2022

    Really I don't know about this stuff. Thank you for sharing

  • Lioness100
    Lioness100Mar 4, 2022

    I ran a quick benchmark and didn't realize how much passing an array of keys helps performance! I'll look out for that in the future, thanks 💪

  • Divjot Singh
    Divjot SinghSep 28, 2024

    Thanks for the article. There is a bug in the above function.
    In Reviving Data section, in line 14 of code, replace parts with...parts. The array needs to be spread when creating a new object.

Add comment