Search code examples
javascriptjsonnode.jsparsingreviver-function

node.js JSON.parse reviver doesn't report duplicate keys?


I'm trying to use a JSON parser which will detect and save duplicate keys. I'm using JSON.parse() in node.js with a reviver, which I am hoping will tell me when I'm getting a duplicate key. However it doesn't. Is there another way? Is there a better JSON parser which handles duplicate keys in a reviver or other argument?

"use strict";

try {
    var content = '{"value": "a", "value": "b", "value": "c" }';
    console.log(content);
    var parsed = JSON.parse(content, function(k, v) {
        console.log(k+"="+JSON.stringify(v));
        return v;
    });
} catch (e) {
    console.log(e);
}

Output is:

{"value": "a", "value": "b", "value": "c" }
value="c"
={"value":"c"}

Solution

  • JSON.parse() parses the string the same way whether or not you provide a reviver function (in other words, it does not switch to a "streaming parser" when a reviver is passed in). Providing a reviver function is just a convenience so as not to have to write the necessary loops yourself.

    There are some streaming JSON parsers on npm, for example: clarinet, JSONStream, and oboe. Here's a little test for those 3:

    var clarinet = require('clarinet').parser();
    var JSONStream = require('JSONStream').parse('*', function (value, path) {
      return { key: path[path.length - 1], value: value, path: path }
    });
    var rs = new (require('stream').Readable)();
    rs._read = function(n) {};
    var oboe = require('oboe')(rs);
    
    var content = '{"value": "a", "value": "b", "value": "c" }';
    
    clarinet.onopenobject = clarinet.onkey = function(k) {
      console.log('clarinet key =', k);
    };
    clarinet.onvalue = function(v) {
      console.log('clarinet value =', v);
    };
    clarinet.write(content).close();
    
    JSONStream.on('data', function(d) {
      console.log('JSONStream data:', arguments);
    }).end(content);
    
    oboe.on('node:*', function(n) {
      console.log('oboe node:', arguments);
    });
    rs.push(content);
    rs.push(null);
    
    // output:
    // clarinet key = value
    // clarinet value = a
    // clarinet key = value
    // clarinet value = b
    // clarinet key = value
    // clarinet value = c
    // JSONStream data: { '0': { key: 'value', value: 'a', path: [ 'value' ] } }
    // JSONStream data: { '0': { key: 'value', value: 'b', path: [ 'value' ] } }
    // JSONStream data: { '0': { key: 'value', value: 'c', path: [ 'value' ] } }
    // oboe node: { '0': 'a', '1': [ 'value' ], '2': [ { value: 'a' }, 'a' ] }
    // oboe node: { '0': 'b', '1': [ 'value' ], '2': [ { value: 'b' }, 'b' ] }
    // oboe node: { '0': 'c', '1': [ 'value' ], '2': [ { value: 'c' }, 'c' ] }
    // oboe node: { '0': { value: 'c' }, '1': [], '2': [ { value: 'c' } ] }