DEV Community

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

Posted on • Originally published at blog.siddu.tech

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

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!

Top comments (25)

Collapse
 
toxictoast profile image
ToxicToast

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

Collapse
 
siddharthshyniben profile image
Siddharth

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

Collapse
 
joelbonetr profile image
JoelBonetR 🥇

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

Collapse
 
siddharthshyniben profile image
Siddharth

I never really thought about that 😂

Collapse
 
toxictoast profile image
Comment marked as low quality/non-constructive by the community. View Code of Conduct
ToxicToast

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

Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇

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

Collapse
 
biomathcode profile image
Pratik sharma

+1

Collapse
 
insign profile image
Hélio oliveira

Agreed. Looks like clickbait.

Collapse
 
siddharthshyniben profile image
Siddharth

All clickbait isn't bad

Collapse
 
lioness100 profile image
Lioness100

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 💪

Collapse
 
cerchie profile image
Lucia Cerchie

🤯 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?

Collapse
 
siddharthshyniben profile image
Siddharth

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.

Collapse
 
cerchie profile image
Lucia Cerchie

Thanks for explaining that!

Collapse
 
sewellstephens profile image
sewellstephens

I would have never guessed the date function used revivers. I made a Zapier integration for my software product for the first time a few months ago and apparently, Zapier only likes Json Arrays for Triggers in the developer platform. They don't accept Objects.

It was also the first API I ever made ✨

Collapse
 
codesnail21 profile image
Vatsal

Awesome 😎

Collapse
 
siddharthshyniben profile image
Siddharth

😎

Collapse
 
lico profile image
SeongKuk Han • Edited

Awesome! Thanks for sharing

Collapse
 
siddharthshyniben profile image
Siddharth

Thanks!

Collapse
 
adam_cyclones profile image
Adam Crockett 🌀

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

Collapse
 
sunflowertoadtheorbiter profile image
SunflowerToadTheOrbiter

Really cool, thanks for sharing!

Collapse
 
siddharthshyniben profile image
Siddharth

Thanks for reading, too!

Collapse
 
elijahtrillionz profile image
Elijah Trillionz

Great writeup.
Thanks for sharing

Collapse
 
5h0bh4nk profile image
Shubh4nk

Awesome

Collapse
 
starboysharma profile image
Pankaj Sharma

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

Collapse
 
siddharthshyniben profile image
Siddharth

Thanks! :D

Some comments may only be visible to logged-in visitors. Sign in to view all comments.