| 'use strict' |
| |
| 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 header, footer |
| 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) |
| if (header) |
| stream.emit('header', header) |
| if (footer) |
| stream.emit('footer', footer) |
| stream.queue(null) |
| }) |
| |
| if('string' === typeof path) |
| path = path.split('.').map(function (e) { |
| if (e === '$*') |
| return {emitKey: true} |
| else 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 (value) { |
| if (!this.root) |
| stream.root = value |
| |
| if(! path) return |
| |
| var i = 0 // iterates on path |
| var j = 0 // iterates on stack |
| var emitKey = false; |
| var emitPath = false; |
| 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)) { |
| setHeaderFooter(c.key, value) |
| return |
| } |
| emitKey = !!key.emitKey; |
| emitPath = !!key.emitPath; |
| 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++; |
| if (!Object.isFrozen(this.stack[j])) |
| this.stack[j].value = null |
| break |
| } else { |
| setHeaderFooter(c.key, value) |
| } |
| j++ |
| } |
| } |
| |
| } |
| |
| // emit header |
| if (header) { |
| stream.emit('header', header); |
| header = false; |
| } |
| if (j !== this.stack.length) return |
| |
| count ++ |
| var actualPath = this.stack.slice(1).map(function(element) { return element.key }).concat([this.key]) |
| var data = value |
| if(null != data) |
| if(null != (data = map ? map(data, actualPath) : data)) { |
| if (emitKey || emitPath) { |
| data = { value: data }; |
| if (emitKey) |
| data["key"] = this.key; |
| if (emitPath) |
| data["path"] = actualPath; |
| } |
| |
| stream.queue(data) |
| } |
| for(var k in this.stack) |
| if (!Object.isFrozen(this.stack[k])) |
| this.stack[k].value = null |
| } |
| 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) |
| count = 0; |
| stream.root = null; |
| } |
| } |
| } |
| |
| parser.onError = function (err) { |
| if(err.message.indexOf("at position") > -1) |
| err.message = "Invalid JSON (" + err.message + ")"; |
| stream.emit('error', err) |
| } |
| |
| return stream |
| |
| function setHeaderFooter(key, value) { |
| // header has not been emitted yet |
| if (header !== false) { |
| header = header || {} |
| header[key] = value |
| } |
| |
| // footer has not been emitted yet but header has |
| if (footer !== false && header === false) { |
| footer = footer || {} |
| footer[key] = value |
| } |
| } |
| } |
| |
| 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 || 'object' === 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 |
| try { |
| var json = JSON.stringify(data, null, indent) |
| } catch (err) { |
| return stream.emit('error', err) |
| } |
| 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 |
| var anyData = false |
| var 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 |
| } |
| |
| |