Further Study: reviver function in JSON.parse()

While solving the exercises of Day 16, I couldn’t understand the concept of reviver parameter of JSON.parse(). In this page, let’s get the point of using the function and look through some examples.

1. Understanding the concept

If a reviver is specified, the value computed by parsing is transformed before being returned. The computed value and all its properties(from properties to their values) are individually run through the reviver.

Then it is called, with the object containing the property being processed as this, and with the property name as a string, and the property value as arguments. If the reviver function returns undefined or if it returns no value(e.g. the execution falls off the end of the function), the property is deleted from the object. Otherwise, the property is redefined to be the return value.

If the reviver only transforms some values and not others, be sure to return all the untransformed values as it is. Otherwise they will be deleted from the resulting object.

JSON.parse('{"p": 5}', (key, value) =>
  typeof value === 'number'
    // for number data type
    ? value * 2
    // for data types except number
    : value
);  // {p: 10}


// the value that isn't a number is deleted before the output
JSON.parse('{"p": 5, "q": "text"}', (key, value) => {
  if (typeof value === 'number') 
    console.log(value * 2);
  }
);  // 10


+ Access to the object being revived

The link is a question at stackflow about the bold part of the upper paragraph. If I call certain keys at some conditionals, the object that is being revived is only the object that is in the same level. The subordinate levels wouldn’t be fully logged.

let aTestStr = '{
  "prop1": "this is prop 1", 
  "prop2": {
    "prop2A": 25, 
    "prop2B": 13, 
    "prop2C": "This is 2-c"
  }
}';
let aTestStr = '{"prop1": "this is prop 1", "prop2": {"prop2A": 25, "prop2B": 13, "prop2C": "This is 2-c"}}';
let aTestObj = JSON.parse(aTestStr, function(key, value) {
  //at this point, 'this' refers to the object being revived
  //E.g., when key == 'prop1', 'this' is an object with prop1 and prop2
  //when key == prop2B, 'this' is an object with prop2A, prop2B and prop2C
    if (key == "prop2B") {
      // the object being revived('this') is an object named 'prop2'
      // add a prop to 'this'
      this.prop2D = 60;
      console.log(this);
    }
});
// {prop2B: 13, prop2C: 'This is 2-c', prop2D: 60}

The interesting part was that this didn’t log all the key-value pairs that I expected. Notice that prop2A is missing. I guess it’s because this is detected after the conditional finds prop2B.

let aTestStr = '{"prop1": "this is prop 1", "prop2": {"prop2A": 25, "prop2B": 13, "prop2C": "This is 2-c"}}';
let aTestObj = JSON.parse(aTestStr, function(key, value) {
  if (key == "prop2C") {
    this.prop2D = 60;
    this.prop2E = "the last key";
    console.log(Object.keys(this));
  }
});
// (3) ['prop2C', 'prop2D', 'prop2E']


let aTestStr = '{"prop1": "this is prop 1", "prop2": {"prop2A": 25, "prop2B": 13, "prop2C": "This is 2-c"}}';
let aTestObj = JSON.parse(aTestStr, function(key, value) {
  if (key == "prop2A") {
    this.prop2D = 60;
    this.prop2E = "the last key";
    console.log(Object.values(this));
  }
});
// (5) [25, 13, 'This is 2-c', 60, 'the last key']


2. Examples of JSON.parse() with reviver function

The reviver funciton is an optional, second parameter in JSON.parse(). Its purpose is to modify the result before returning, acting as a filter function. All parsed values are passed through this reviver function in key-value pair before being returned.

JSON.parse(jsonString, function (key, value) {
 	// all key-value pairs in the JSON object are passed here
	
	// return value for the key
	return value;
});

If a key-value pair passing through this reviver method causes an error, or the reviver method returns undefined for any pair, that key-value pair is deleted from the final parsed JSON object.


Example 01. An object

let jsonString = '{"1": "A", "2": "B", "3": "D", "4": "C"}';
let parsedJson = JSON.parse(jsonString, function (key, value) {
  if (key == 3) 
    return "C";

  if (key == 4)
    return "D";
  
  return value;
});

console.log(parsedJson);
// {1: 'A', 2: 'B', 3: 'C', 4: 'D'}

If I use === in the place of ==, the values in parsedJson wouldn’t change. I guess it’s because === also checks if the types of values are the same. Since the key is a string and 3 is a number, it wouldn’t exactly match.


Example 02. Objects inside an array

let jsonString = '[
  {
    "team": "Ferrari", 
    "drivers": [
      {"name": "Vettel"}, 
      {"name": "Raikkonen"}
    ]
  }
]'
let jsonString = '[{"team": "Ferrari", "drivers": [{"name":"Vettel"}, {"name":"Raikkonen"}]}]';
let parsedJson = JSON.parse(jsonString, function(key, value) {
    if(value === "Ferrari")
        return "Scuderia Ferrari";

    if(value === "Raikkonen")
        return "Leclerc";

    return value; 
});

console.log(parsedJson);
// [{"team": "Scuderia Ferrari", "drivers": [{"name": "Vettel"}, {"name": "Leclerc"}]}]

From the upper example, I can see that all the key-value pairs all run through the reviver function. It means that I don’t have to track down the properties to modify the values.


Example 03. Nested objects

When the JSON is nested, reviver begins with the most inner properties.

let jsonString = 
'{
  "one": 1,
  "two": 2, 
  "three": {
    "four": 4, 
    "five": {
      "six": 6
    }
  }
}';
let jsonString = '{"one": 1, "two": 2, "three": {"four": 4, "five": {"six": 6}}}';
let parsedJson = JSON.parse(jsonString, function(key, value) {
  console.log(key);
  return value;
});
/*
  one
  two
  four
  six
  five
  three
  ""
*/

The last key is always a blank(" ").



Leave a comment