JSON

When you want to send some request to the server, in most situation you will send some data. But in which format you should send the data? Most of the time, these data contains lot of information. The format you are sending should be recognized by the backend server and interpret as well. If you send Javascript Object, there are few languages which cannot recognize this format. So Javascript Object is not right format to send. You can send string, but converting object data to string and parse it in the backend to the right data type is not a very convenient thing. For example -

let user = {
  name: "John",
  age: 30,

  toString() {
    return `{name: "${this.name}", age: ${this.age}}`;
  }
};

alert(user); // {name: "John", age: 30}

…But in the process of development, new properties are added, old properties are renamed and removed. Updating such toString every time can become a pain. We could try to loop over properties in it, but what if the object is complex and has nested objects in properties? We’d need to implement their conversion as well. And, if we’re sending the object over a network, then we also need to supply the code to “read” our object on the receiving side.

For small information like just a name, or number, string format is fine. But when there are lots of information, String is inappropriate. JSON solves this problem. It is a format which is recognized by most programming language and easy to work with.

The JSON (JavaScript Object Notation) is a general format to represent values and objects. Initially it was made for JavaScript, but many other languages have libraries to handle it as well. So it’s easy to use JSON for data exchange when the client uses JavaScript and the server is written on Ruby/PHP/Java/Whatever.

JSON.stringify()

This method converts objects into JSON.

let student = {
  name: 'John',
  age: 30,
  isAdmin: false,
  courses: ['html', 'css', 'js'],
  wife: null
};

let json = JSON.stringify(student);

alert(typeof json); // we've got a string!

alert(json);
/* JSON-encoded object:
{
  "name": "John",
  "age": 30,
  "isAdmin": false,
  "courses": ["html", "css", "js"],
  "wife": null
}
*/

The method JSON.stringify(student) takes the object and converts it into a string.

The resulting json string is a called JSON-encoded or serialized or stringified or marshalled object. We are ready to send it over the wire or put into a plain data store.

Please note that a JSON-encoded object has several important differences from the object literal:

Convert primitive type

Using JSON.stringify() not only you can convert Object to string. But also you can convert other data type too like String, Array, Boolean, Numbers etc.

// a number in JSON is just a number
alert( JSON.stringify(1) ) // 1

// a string in JSON is still a string, but double-quoted
alert( JSON.stringify('test') ) // "test"

alert( JSON.stringify(true) ); // true

alert( JSON.stringify([1, 2, 3]) ); // [1,2,3]

Nested Objects are converted too

The great thing is that nested objects are supported and converted automatically.

let meetup = {
  title: "Conference",
  room: {
    number: 23,
    participants: ["john", "ann"]
  }
};

alert( JSON.stringify(meetup) );
/* The whole structure is stringified:
{
  "title":"Conference",
  "room":{"number":23,"participants":["john","ann"]},
}
*/

Few properties are skipped by JSON.stringify

There are few properties that doesn't get included during the conversion -

let user = {
  sayHi() { // ignored
    alert("Hello");
  },
  [Symbol("id")]: 123, // ignored
  something: undefined // ignored
};

alert( JSON.stringify(user) ); // {} (empty object)

JSON.stringify fails for Circular Reference

If your object references in a circular direction, then the method JSON.stringify() fails to convert to JSON. Consider the following example -

let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  participants: ["john", "ann"]
};

meetup.place = room;       // meetup references room
room.occupiedBy = meetup; // room references meetup

The property place of meetup object has the reference to the object room. Now occupiedBy property of room object refers to the object meetup. It mean both object refers to both object. If you try to convert that object to JSON, you will get an error.

JSON.stringify(meetup); // Error: Converting circular structure to JSON

toJSON()

Like toString for string conversion, an object may provide method toJSON for to-JSON conversion. JSON.stringify automatically calls it if available.

let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  date: new Date(Date.UTC(2017, 0, 1)),
  room
};

alert( JSON.stringify(meetup) );
/*
  {
    "title":"Conference",
    "date":"2017-01-01T00:00:00.000Z", 
    "room": {"number":23}               
  }
*/

Here we can see that date became a string. That’s because all dates have a built-in toJSON method which returns such kind of string.

Now let’s add a custom toJSON for our object room:

let room = {
  number: 23,
  toJSON() {
    return this.number;
  }
};

let meetup = {
  title: "Conference",
  room
};

alert( JSON.stringify(room) ); // 23

alert( JSON.stringify(meetup) );
/*
  {
    "title":"Conference",
    "room": 23
  }
*/

As we can see, toJSON is used both for the direct call JSON.stringify(room) and for the nested object JSON.stringify(meetup).

JSON.parse()

The method JSON.parse() converts a JSON string to JS object. It does the reverse of the JSON.stringify().

let value = JSON.parse(str[, reviver]);

For example -

// stringified array
let numbers = "[0, 1, 2, 3]";

numbers = JSON.parse(numbers);

alert( numbers[1] ); // 1


// Also works for Nested Object -
let user = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }';

user = JSON.parse(user);

alert( user.friends[1] ); // 1

Using reviver

The JSON.parse() method takes an optional function(key,value) as a second argument that will be called for each (key, value) pair and can transform the value. This argument is called Reviver.

Imagine, we got a stringified meetup object from the server.

// title: (meetup title), date: (meetup date)
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';

…And now we need to deserialize it, to turn back into JavaScript object.

Let’s do it by calling JSON.parse:

let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';

let meetup = JSON.parse(str);

alert( meetup.date.getDate() ); // Error!

The value of meetup.date is a string, not a Date object. How could JSON.parse know that it should transform that string into a Date?

Let’s pass to JSON.parse the reviving function that returns all values “as is”, but date will become a Date:

let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';

let meetup = JSON.parse(str, function(key, value) {
  if (key == 'date') return new Date(value);
  return value;
});

alert( meetup.date.getDate() ); // now works!

By the way, that works for nested objects as well:

let schedule = `{
  "meetups": [
    {"title":"Conference","date":"2017-11-30T12:00:00.000Z"},
    {"title":"Birthday","date":"2017-04-18T12:00:00.000Z"}
  ]
}`;

schedule = JSON.parse(schedule, function(key, value) {
  if (key == 'date') return new Date(value);
  return value;
});

alert( schedule.meetups[1].date.getDate() ); // works!