Michael Ryan Soileau Michael Ryan Soileau - 1 month ago 6
JSON Question

Unexpected behavior in JSON.parse Reviver function, deleting the object and not the key properties

JSBin link so that you can run the code quickly.

JSbinhere

The problem is in the comments, but from what the documentation states about how the reviver (that name is terrible), works, if you do not return a value or if you return

undefined
then that property should be removed from the object. If you return the untransformed value, it stays the same.

Yet when I test it out, it looks like the entire object gets removed. The first example works just fine, the even numbers are converted to their negative and the odd numbers are unchanged.

But in the second example, I don't even get an object back, just undefined. So am I misreading the documentation or is something else wrong?

The result is just undefined in the second example.

var obj = {
one: 1,
innerObj: {
two: 2,
four: 4
},
two: 2,
three: 3,
four: 4
},
b = {},
json = JSON.stringify(obj);
/**
* This works as expected.
*/
b = JSON.parse(json, function (name, value) {
if (value % 2 === 0) {
return -value;
}
return value;
});
console.log(b);
/**
[object Object] {
four: -4,
innerObj: [object Object] {
four: -4,
two: -2
},
one: 1,
three: 3,
two: -2
}
*/

obj = {
one: 1,
innerObj: {
two: 2,
four: 4
},
two: 2,
three: 3,
four: 4
};
b = {};
json = JSON.stringify(obj);
/**
* This does not work as expected, instead of deleting the property on the object, the entire object returns undefined.
*/
b = JSON.parse(json, function (name, value) {
if (value % 2 === 0) {
return -value;
}

});
console.log(b);
// undefined

Answer

Your second example left return value; off.

But even tough you did that, it should only have removed the properties that returned undefined as value, and I think you found a bug (may be?).

From one of MDN JSON.parse examples it says that last key is "" when JSON.parse is called

I managed to reproduce your code with the undefined error and it seems that if you return the value for the "" key, like

if (name == "") {
  return value;
}

it appears to work as expected.

obj = {
   one: 1,
   innerObj: {
     two: 2,
     four: 4
   },
   two: 2,
   three: 3,
   four: 4
};
b = {};
json = JSON.stringify(obj);


/**
* This does not work as expected, instead of deleting the property on the object, the entire object returns undefined.
*/
b = JSON.parse(json, function (name, value) {
  if (value % 2 === 0) {
    return -value;
  }
  if (name == "") {
    return value;
  }
});
console.log(b);
// { two: -2, four: -4 }

EDIT:

So, reading ECAMScript JSON.parse(text [, reviver]) specification, it seems that this is the expected behavior. When it describes the behavior f calling the reviver function, the last step after calling all DefineOwnProperty items is

Return the result of calling the abstract operation Walk, passing root and the empty String.

So, when you don't return a value for the '' name at your reviver function, it understand that it should remove that, which stands for the whole object to be returned, resulting in it being undefined for the JSON.parse return.

That explains the concern on MDN docs when it says

be certain to return all untransformed values as-is

But I agree that it should be more explicit about how these small nuances work.

Comments