#! /usr/bin/env node var Parser = require('jsonparse') , through = require('through') /* the value of this.stack that creationix's jsonparse has is weird. it makes this code ugly, but his problem is way harder that mine, so i'll forgive him. */ exports.parse = function (path, map) { var parser = new Parser() var stream = through(function (chunk) { if('string' === typeof chunk) chunk = new Buffer(chunk) parser.write(chunk) }, function (data) { if(data) stream.write(data) stream.queue(null) }) if('string' === typeof path) path = path.split('.').map(function (e) { if (e === '*') return true else if (e === '') // '..'.split('.') returns an empty string return {recurse: true} else return e }) var count = 0, _key if(!path || !path.length) path = null parser.onValue = function () { if (!this.root && this.stack.length == 1) stream.root = this.value if(! path) return var i = 0 // iterates on path var j = 0 // iterates on stack while (i < path.length) { var key = path[i] var c j++ if (key && !key.recurse) { c = (j === this.stack.length) ? this : this.stack[j] if (!c) return if (! check(key, c.key)) return i++ } else { i++ var nextKey = path[i] if (! nextKey) return while (true) { c = (j === this.stack.length) ? this : this.stack[j] if (!c) return if (check(nextKey, c.key)) { i++; break} j++ } } } if (j !== this.stack.length) return count ++ var data = this.value[this.key] if(null != data) if(null != (data = map ? map(data) : data)) stream.queue(data) delete this.value[this.key] } parser._onToken = parser.onToken; parser.onToken = function (token, value) { parser._onToken(token, value); if (this.stack.length === 0) { if (stream.root) { if(!path) stream.queue(stream.root) stream.emit('root', stream.root, count) count = 0; stream.root = null; } } } parser.onError = function (err) { stream.emit('error', err) } return stream } function check (x, y) { if ('string' === typeof x) return y == x else if (x && 'function' === typeof x.exec) return x.exec(y) else if ('boolean' === typeof x) return x else if ('function' === typeof x) return x(y) return false } exports.stringify = function (op, sep, cl, indent) { indent = indent || 0 if (op === false){ op = '' sep = '\n' cl = '' } else if (op == null) { op = '[\n' sep = '\n,\n' cl = '\n]\n' } //else, what ever you like var stream , first = true , anyData = false stream = through(function (data) { anyData = true var json = JSON.stringify(data, null, indent) if(first) { first = false ; stream.queue(op + json)} else stream.queue(sep + json) }, function (data) { if(!anyData) stream.queue(op) stream.queue(cl) stream.queue(null) }) return stream } exports.stringifyObject = function (op, sep, cl, indent) { indent = indent || 0 if (op === false){ op = '' sep = '\n' cl = '' } else if (op == null) { op = '{\n' sep = '\n,\n' cl = '\n}\n' } //else, what ever you like var first = true , anyData = false stream = through(function (data) { anyData = true var json = JSON.stringify(data[0]) + ':' + JSON.stringify(data[1], null, indent) if(first) { first = false ; this.queue(op + json)} else this.queue(sep + json) }, function (data) { if(!anyData) this.queue(op) this.queue(cl) this.queue(null) }) return stream } if(!module.parent && process.title !== 'browser') { process.stdin .pipe(exports.parse(process.argv[2])) .pipe(exports.stringify('[', ',\n', ']\n', 2)) .pipe(process.stdout) }