13

Here is my code:

BAD = {
          "a": "2",
          "b": 1,
          "c": "Nexus",
          "d": "Alligator",
          "e": 5,
          "f": 1431807036,
          "g": {
                    "2": {
                            "w": 17,
                            "b": 5
                         }
               }
      }

console.log(JSON.stringify(BAD, ['a', 'b', 'c', 'd', 'e', 'g']));

http://jsfiddle.net/whv7x6xc/1/

The keys a, b, c, d, e, and g are the only ones being stringified which is nice, but there is one problem. It's ignoring the object that is assigned to g.

enter image description here

But, if you do: console.log(JSON.stringify(BAD)); it shows the proper stringified version.

Edit: w and b are dynamic and changed periodically (come and go) though, so I cannot just hard-code them in.

SherylHohman
  • 16,580
  • 17
  • 88
  • 94
NiCk Newman
  • 1,716
  • 7
  • 23
  • 48
  • 1
    Well, that object in the `g` property only has a `"2"` property, which is not in your whitelist of property names. So why do you expect it to show up? – Bergi May 16 '15 at 20:25
  • Because `all properties of the object are included in the resulting JSON string.` clearly states all properties of the object (in my case `g`) are included? – NiCk Newman May 16 '15 at 20:28
  • Where is that stated? It's utterly wrong (about non-enumerable, inherited, non-JSON-valued properties and probably more). – Bergi May 16 '15 at 20:33
  • @NiCkNewman you must provide properties for all the objects you want white-listed. This includes keys for the `BAD.g` object and `BAD.g.2`. – Dom May 16 '15 at 20:35
  • @Bergi, I got it [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) under replacer, it says all properties from an object in the replacer are included. – NiCk Newman May 16 '15 at 20:37
  • @NiCkNewman: Well, the whole sentence begins with "*If this value is null or not provided, …*". And you do provide a value for the `replacer` parameter – Bergi May 16 '15 at 20:42
  • Exactly! I'm not providing `w` or `b` through the replacer, so they should be included right? – NiCk Newman May 16 '15 at 20:43
  • 3
    No, it's not talking about values in the array, it is talking about whether providing or not the array itself as an argument to `JSON.stringify`. If you use `console.log(JSON.stringify(BAD))`, it will by default include all visible properties. If you do pass a `replacer` (i.e. a function or an array), then all properties will be subject to the replacement rules. – Bergi May 16 '15 at 20:46

3 Answers3

15

JSON replacer is recursive. This then means it uses the same array for all objects it comes across. In other words, it uses the same values to map BAD.g's value. Since, Bad.g's value does not have any keys matching the ones you provided, nothing will get mapped properly. This means we have to add "2" to your array. Now "2" also has an object with different keys. We have already accounted for "b", so now we just need to add "w" to your array.

When providing an array to the replacer, best to think of the array as a list of all the keys you want mapped.

DEMO: http://jsfiddle.net/dirtyd77/whv7x6xc/4/

console.log(JSON.stringify(BAD, ['a', 'b', 'c', 'd', 'e', 'g', '2', 'w']));

Hope this helps and let me know if you have any questions.


Here is also a snippet from the documentation,

If you return any other object, the object is recursively stringified into the JSON string, calling the replacer function on each property, unless the object is a function, in which case nothing is added to the JSON string.


In the case for dynamic keys, you can always create a function to push to an array and use that array for your whitelist. Here is an example:

DEMO: http://jsfiddle.net/dirtyd77/whv7x6xc/5/

var BAD = {
    "a": "2",
    "b": 1,
    "c": "Nexus",
    "d": "Alligator",
    "e": 5,
    "f": 1431807036,
    "g": {
        "2": {
            "w": 17,
            "b": 5
        }
    }
}

var whitelist = [];

iterate(BAD);

console.log(whitelist, JSON.stringify(BAD, whitelist));

function iterate(obj){
    for(var key in obj){
        // may have to do some checking to ignore any keys you don't care about
        whitelist.push(key);
        // if value is an object, will use this same function to push to whitelist array
        if(typeof obj[key] === 'object'){
            return iterate(obj[key]);
        }
    }
}

You can also just use your existing whitelist and just append keys on your g key (given that isn't dynamic):

DEMO: http://jsfiddle.net/dirtyd77/whv7x6xc/6/

var whitelist = ['a', 'b', 'c', 'd', 'e', 'g'];

iterate(BAD.g);

console.log(whitelist, JSON.stringify(BAD, whitelist));

function iterate(obj){
    for(var key in obj){
        whitelist.push(key);
        if(typeof obj[key] === 'object'){
            return iterate(obj[key]);
        }
    }
}
Dom
  • 38,906
  • 12
  • 52
  • 81
  • From what I understand from the documentation it says: `all properties of the object are included in the resulting JSON string.`, which in my case, `g`'s properties would be included. also, `2` and `w` are dynamic (based on my players equipped items ^^), so I cannot hard-code those values in, would I need to make a temporary array and push the properties of `g` (I can do this with Object.keys) in to send to the replacer? – NiCk Newman May 16 '15 at 20:35
  • @NiCkNewman edited it, however, the `iterate` function I created pushes all keys to the whitelist. You may have to do some checking to ignore keys you don't care about. Let me know if you need anything else! – Dom May 16 '15 at 20:43
  • Or you can also do this... you can set `var whitelist = ['a', 'b', 'c', 'd', 'e', 'g'];` and just iterate over BAD.g - `iterate(BAD.g);` http://jsfiddle.net/dirtyd77/whv7x6xc/6/ – Dom May 16 '15 at 20:46
  • Okay, I'm an idiot. I thought when the documentation said `recursive` that EVERYTHING under that object is stringified. But that's my problem because by definition `recursive` is: `relating to or involving a program or routine of which a part requires the application of the whole, so that its explicit interpretation requires in general many successive executions.` I kept thinking that the repeating part means stringifying all the properties of that object, but recursive means just repeating basically. And in this case, the replacer is just repeating it's replacer of the key for it's parameter – NiCk Newman May 16 '15 at 21:27
6

The replacer argument is deep, in that it affects the stringification of all properties, even if they're not on the first level. If you add "2" to the array, you'll see that the object is stringified, minus "w", because "w" is not allowlisted by replacer.

JSON.stringify(BAD, ['a', 'b', 'c', 'd', 'e', 'g', '2'])

Consider why you are using replacer. If you are using it to blocklist, instead of allowlist, you might be better served by passing in a function as opposed to an array.

JSON.stringify(BAD, function replacer(key, value) {
  var blocklist = ['b', 'g']
  return blocklist.indexOf(key) === -1 ? value : undefined
})
steveluscher
  • 4,144
  • 24
  • 42
Dylan Watt
  • 3,357
  • 12
  • 16
2

Add two more keys to the parameters like:

JSON.stringify(BAD, ['a', 'b', 'c', 'd', 'e', 'g', '2', 'w'])

Output

Fiddle

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Abhi
  • 4,123
  • 6
  • 45
  • 77