JSON is probably one of the most underrated programming language in existence. Not only is it so widespread, it’s lightweight and no-nonsense demeanor makes JSON feel almost native to whichever language it pairs up with.

Yes. That’s right. JSON is it’s own language, despite becoming somewhat synonymous with JavaScript. 

Over the years, other languages have picked up support and steered away from the other potential option like XML and YAML. It didn’t take long for JSON to officially take over everything and act as the bridge between backends and frontends, frameworks and libraries, transmitting and translating data back and forth between the different places and spaces.

So where does the drama with toString() come in?

Well, let’s start by pretending that we need to turn an object into a string.


Making objects into strings

There are times where you just want to return a string instead of a complex object. For example, you want to send it over a network or output it for logging purposes.

Rather than getting that annoying [object Object] output via console log, you just want it to print as a string so you can quickly debug and scan the returned data.

To do this, in theory, use the toString() method. But the syntax for this can end up being cumbersome in the long run.

How and why?

The thing with toString() and objects is that it returns the literal description rather than the contents inside. So rather than getting something like {firstName:"Jane", lastName:"Doe"} you get the lovely [object Object]

This, naturally, isn’t that helpful, especially when you want to do something directly with that string.

For toString() to work properly in turning objects into strings, you need to break it up and manually abstract out each key-pair values.

For example:

test = {website:"dottedsquirrel.com", 
toString(){
return `{website:"${this.website}"}`;
}}
console.log(test.toString());

Yes, toString() does work elegantly with numbers, but not so much when it comes to objects.

The above example is a simplified implementation — but imagine a full data set with more than a dozen key-pair values. Thinking about manually mapping out each value is enough to make any developer flinch.

It gets a bit more complicated if you’ve received data from somewhere else.

So rather than battle with it, JavaScript has two methods to deal directly with JSON and they are: JSON.stringify and JSON.parse


Let’s start with JSON.stringify

The syntax for these two methods is simple. You just call it and pass in the object you want to transform. The outputted value will either be a string or an object. 

For example:

let someObject = { 
url:"dottedsquirrel.com",
siteName:"Dotted Squirrel",
tagline:"Becoming Better Developers Together"
}
let stringMeeee = JSON.stringify(someObject);
console.log(typeof stringMeeee, stringMeeee);

When an object is stringified in this manner, it’s called a JSON-encoded or serialized object — that is, it was originally an object but got turned into a plain data format.

How do you tell the difference between a JSON-encoded object and a JavaScript object?

The devil is in the details.

With JSON-encoded strings, single quotes or backticks because double quotes. So 'dottedsquirrel' becomes "dottedsquirrel"

It’s the same with object property names. While your JavaScript object may work just fine without " " on the object property name, JSON enforces the usage of double quotes. This means you your properties end up from this name: 'Aphinya' to this "name":"Aphinya"

The beauty of JSON.stringify is that, in addition to objects, it also works on arrays, other strings, numbers, boolean values and null. 

Another thing to note is that because JSON is its own language, JSON.stringify will ignore everything else and will only transform data-only into strings.

So this means, no methods, no symbolic properties or properties that stores undefined

For example:

let someObject = {
makeNoise(){ return 'woot'; }, //will be ignored
someRandomThing: undefined, //will be ignored
thisOne: 'is ok',
why: 'because hashtag data'
}
console.log(JSON.stringify(someObject));

Is there anything else you should know about stringify? Yes. Yes there is.

Most of the time, the first argument for JSON.stringify does most of the hard work. However, JSON.stringify takes a total of three arguments. So it actually looks something like this:

JSON.stringify(value, replacer, space)

Let’s start with replacer

Ok. So you’ve got an object but you don’t want it to return everything. How do you do this without modifying the original object itself?

JSON.stringify lets you do this with replacer. 

Let’s start off with some data.

let restaurant = {
name: "Prego",
location: "Ponsonby",
city: "Auckland",
country: "New Zealand",
staff: { fullTime: 15, partTime: 6}
}
let dishList = [
{dish: "MARGHERITA",
description: "Napoli style"},
{dish: "CAPRICCIOSA",
description: "Napoli style"},
]
restaurant.menu = dishList;

Now, let’s say you don’t want to include the city in the returned string. You can do this by listing out only the keys you want to be included.

So your JSON.stringify ends up looking like this:

console.log(JSON.stringify(restaurant, ['name','location','country','menu', 'dish', 'description']));

Yes. This does look a bit necessarily long and can be an issue if you’re dealing with a large object. What do you do in that scenario?

Well, you can run an explicit replacer() function inside JSON.stringify

replacer() will expose the key and value to you and if you set the logic to return undefined for certain values, it will exclude it from the final output.

Here’s an example code:

console.log(JSON.stringify(restaurant, function replacer(key, value){
if(key == 'city'){ return undefined};
return value;
})
);

What about nesting?

This entire time, your returned string would have been formatted as one long line of text. For small objects, it’s not too bad. For bigger objects, the pain of reading it increases somewhat.

The third and final parameter for JSON.stringify() sorts out this formatting issue by telling the method how many levels of nesting to format the final output.

In the restaurant example above, we can see that it’s nested at level 2.

So to translate and tell JSON.parse this, you can write something like this:

console.log(restaurant, null, 2 );

Just a note that null stands for ‘no value’, so when you put it through, it’ll just continue on as if no filtering is required. 


I like to…parse it, parse it!

So you’ve received a JSON encoded string, notably identified through the rediculous about of "\" when it gets printed via console log.

Most of the time, everything encased around double quote marks is often the first dead giveaway that the string is a JSON encoded string.

To turn it back into an object, the reverse for JSON.stringify is JSON.parse

It looks something like this:

let someStringArrayThing = "[0, 1, 2, 3]";
let turnItBack = JSON.parse(someStringArrayThing);
console.log(turnItBack);

You can also do this with objects.

The thing with JSON is that it can be as complex as you need it to be, on condition that it follows the correct syntax. So no mixing single quotes with double quotes and no comments.

Its strict nature may seem annoying at times, especially when you have to manually create JSON objects. However, this strictness is the perk of why JSON is super reliable and fast.


It’s time we talked about using revivers

It’s one thing to turn data in strings, but what if the string transformed is a bit more complicated? How will JSON.parse know?

The quick answer is, it doesn’t. 

How do you solve this?

You tell it what the data type is.

How do you do this?

By using a thing called revive

Let’s take the date object as a prime example for using revive in JSON.parse

let someStringThing = '{"author":"AD","publishedDate":"2020-04-20T11:02:17.023Z"}';

Let’s say we use JSON.parse on this fabulous someStringThing

let turnMeBack = JSON.parse(someStringThing);

In theory, if JSON parse knew exactly what to do, it should turn the publishedDate back into a Date object, but it doesn’t. Why? Because it simply doesn’t know that it’s a Date object.

This means you can’t do this:

console.log(turnMeBack.date.getDate());

This is where revive comes in handy.

In truth, JSON.parse actually takes in two arguments — your JSON encoded string and a reviving function.

What this reviving function does is tell the parse method what data type a particular value is. It’s sort of like a meta tag against your data and stopping it returning as-is.

Here’s how to write a reviving function:

let parseMeAgain = JSON.parse(someStringThing, function(key, value){
if (key == 'publishedDate'){ return new Date(value)}
return value; });

If you have more than one case, you can either add more if statements or turn it into a switch syntax.


That’s basically everything on the topic of JSON.stringify and JSON.parse 

JSON is actually really cool but a lot of people tend to only the first argument for stringify and parse

I hope you learned something new from the piece, and maybe some thinking points on how to deal with data on your current and future projects.

Featured image credit: Knot by Mattie Lynch

Comments

0 comments