JSON for JS devs

JSON , JSON , JSON deep dive in JS

What is a JSON

type JSONPrimitive = string | boolean | number | null;
type JSONObject = { [k:string]: JSONValue };
type JSONArray = JSONValue[];
type JSONValue = JSONArray | JSONObject | JSONPrimitive;

Static function of JSON

πŸ’‘
JSON.stringify(data:any,replacer:function,format:number)

The JSON (JavaScript Object Notation) is a general format to represent values and objects. It is described in the RFC 4627 standard. 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 in Ruby, PHP, Java, whatever.

JavaScript provides methods:

  • JSON.stringify to convert objects into JSON.

  • JSON.parse to convert JSON back into an object.

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

let json = JSON.stringify(student);

console.log(typeof json); // we've got a string!

console.log(json);
/* JSON-encoded object:
'{
   "name" : "Vince",
   "age":30,
   "isAdmin":false,
   "course":['html','css','js'],
   "spouse":null
}'πŸ’‘
πŸ’‘
The object will be converted to a string wrapped around a single quote, the key will be a string with a double quote (no single quote,no backtick), and values will remain

Different between JSON.stringify obj and arr

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

JSON.stringify(meetup);
'{"title":"Conference",
  "room":
   {"number":23,
   "participants":["john","ann"]
 }
 }'

JSON.stringify(meetup.room.participants);
'["john","ann"]'

JSON.stringify can be applied to primitives as well.

JSON supports the following data types:

  • Objects { ... }

  • Arrays [ ... ]

  • Primitives:

    • strings,

    • numbers,

    • boolean values true/false,

    • null.

// a number in JSON is just a number
console.log( JSON.stringify(1) ) // '1'

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

console.log( JSON.stringify(true) ); // 'true'

console.log( JSON.stringify([1, 2, 3]) ); // '[1,2,3]'

Special case for string

// single quote can contain double quote
// '"a":5' but ""a":5" is invalid string
JSON.stringify('Hello "WORLD"');
'"Hello \\"WORLD\\""'

JSON is meant to be used for data-exchange so some of the data types in JS can't fit in and will be skipped by JSON

Namely:

  • Function properties (methods).

  • Symbolic keys and values [Symbol.iterator]()'

  • Properties that storeundefined.

πŸ’‘
It also supports null but not undefined :) '{ "name" : null }' is OKAY because null in an "object"
  • Only JSON-serializable values (i.e. boolean, number, null, array, object) will be present in the input value.
let user = {
  sayHi() { // ignored
    alert("Hello");
  },
  [Symbol("id")]: 123, // ignored
  something: undefined // ignored
};

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

Nested object

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

JSON.stringify(meetup);

' {"title": "Conference",
 "room":{ 
          "number":23,
          "participants":["john","ann"] 
        }
}'
πŸ’‘
This is still a string :) the object is surrounded by ' ' everything inside is a string even-though {"number": 5, "participants": ["john","ann"] may look like an object but it's inside a string
// objects
"{" + '"' + "a" + '":' + 5 + "}";
'{"a":5}'

// arrays
"[" + "1" + "2" +"3" + "]"
'[123]'
πŸ’‘
Error: Converting circular structure to JSON

Implement JSON.stringify

Something to keep in mind when implementing JSON.stringify

Doule quote is single quote in string

πŸ’‘
"{" + properties.join(",") + "}" => '{ "key" : value }'

String + String will be string

πŸ’‘
"{" + '1 2 3' + "}" => '{ 1 2 3}'

JSON.stringify string

πŸ’‘
const value = 'He said, "Hello!"';
const value = 'He said, "Hello!"';
return '"' + value.replace(/"/g, '\\"') + '"';
// Results in: "He said, \"Hello!\""
const jsonStringify = (value) => {
  if (value === null) {
    return "null";
  }
  if (
    typeof value === "boolean" ||
    typeof value === "number"
  ) {
    return String(value);
  }
  if(typeof value === "string") {
    const encapsulated = value.replace(/"/g, '\\"');
    return '"' + encapsulated + '"' 
  }

  // Convert to string to do string tempolation

  if (Array.isArray(value)) {
    const elements = value.map((val) => jsonStringify(val));
    return "[" + elements.join(",") + "]";
  }
  if (typeof value === "object") {
    const keys = Object.keys(value);
    const properties = keys
      .map((key) => {
        const val = jsonStringify(value(key));
        if (val !== undefined) {
          return '"' + key + '":' + val;
        }
      })
      .filter((prop) => prop !== undefined);
    return "{" + properties.join(",") + "}";
  }
  return undefined;
};

Transforming replacer

let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  participants: [{name: "John"}, {name: "Alice"}],
  place: room // meetup references room
};

room.occupiedBy = meetup; // room references meetup

console.log( JSON.stringify(meetup, function replacer(key, value) {
  return (key == 'occupiedBy') ? undefined : value;
  // If we return a value of undefined -> against JSON rules
  // It will be removed from JSON
}));



{"title":"Conference","participants":[{"name":"John"},
                      {"name":"Alice"}],"place":{"number":23}}
πŸ’‘
We can filter out the value we want to have in a JSON format

Formatting:space

The third argument of JSON.stringify(value,replacer,space) is the number of spaces to use for pretty formatting

let user = {
  name: "John",
  age: 25,
  roles: {
    isAdmin: false,
    isEditor: true
  }
};

alert(JSON.stringify(user, null, 2));
/* two-space indents:
'{
  "name": "John",
  "age": 25,
  "roles": {
    "isAdmin": false,
    "isEditor": true
  }
}'
*/

/* for JSON.stringify(user, null, 4) the result would be more indented:
'{
    "name": "John",
    "age": 25,
    "roles": {
        "isAdmin" : false,
        "isEditor": true
    }
}'
*/

Custom "to JSON"

An object may provide a custom toJSON methods

let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  date: new Date(Date.UTC(2017, 0, 1)), //Date.prototype.JSON converts to string
  room
};

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

We can write our own custom JSON to convert the object to the type we want

JSON.parse

To decode a JSON string, we need another method named JSON.parse

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

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

numbers = JSON.parse(numbers);

alert( numbers[1] ); // 1

let userData = '
  { "name": "John", 
     "age": 35, 
     "isAdmin": false, 
     "friends": [0,1,2,3] }';

let user = JSON.parse(userData);

alert( user.friends[1] ); // 1
πŸ’‘
The JSON may be as complex as necessary; objects and arrays can include other objects and arrays. But they must obey the same JSON format.
let json = `{
  name: "John",                     // mistake: property name without quotes
  "surname": 'Smith',               // mistake: single quotes in value (must be double)
  'isAdmin': false                  // mistake: single quotes in key (must be double)
  "birthday": new Date(2000, 2, 3), // mistake: no "new" is allowed, only bare values
  "friends": [0,1,2,3]              // here all fine
}`;

Using reviver as middleware

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

let meetup = JSON.parse(str);

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

meetup.date // '2017-11-30T12:00:00.000Z'

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!

Summary

  • JSON is a data format that has its own independent standard and libraries for most programming languages.

  • JSON supports plain objects, arrays, strings, numbers, booleans, and null.

  • JavaScript provides methods JSON.stringify to serialize into JSON and JSON.parse to read from JSON.

  • Both methods support transformer functions for smart reading and writing.

  • If an object has toJSON, then it is called by JSON.stringify.

Exercises

let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  occupiedBy: [{name: "John"}, {name: "Alice"}],
  place: room
};

// circular references
room.occupiedBy = meetup;
meetup.self = meetup;

console.log(JSON.stringify(meetup,(key,value)=>{
    if(key == "occupiedBy" | key == "self") {
        return undefined 
    }
    else {
        return value;
    }
}));
// {"title":"Conference","place":{"number":23}}
let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  occupiedBy: [{name: "John"}, {name: "Alice"}],
  place: room
};

// circular references
room.occupiedBy = meetup;
meetup.self = meetup;

console.log(JSON.stringify(meetup, (key,value)=>{
    if(key != "" && value == meetup ) {
        return undefined; 
    }
    else {
        return value;
    }
}));

// {"title":"Conference","occupiedBy":[{"name":"John"},
// {"name":"Alice"}],"place":{"number":23}}
Β