diff --git a/lib/parser.js b/lib/parser.js index 192382d3..08935917 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1,7 +1,7 @@ // Generated by CoffeeScript 1.12.7 (function() { "use strict"; - var bom, defaults, events, isEmpty, processItem, processors, sax, setImmediate, + var bom, defaults, events, isEmpty, isValidKey, processItem, processors, sax, setImmediate, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty; @@ -22,6 +22,10 @@ return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0; }; + isValidKey = function(key) { + return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; + }; + processItem = function(processors, item, key) { var i, len, process; for (i = 0, len = processors.length; i < len; i++) { @@ -92,6 +96,9 @@ }; Parser.prototype.assignOrPush = function(obj, key, newValue) { + if (!isValidKey(key)) { + return; + } if (!(key in obj)) { if (!this.options.explicitArray) { return obj[key] = newValue; @@ -141,21 +148,23 @@ this.saxParser.onopentag = (function(_this) { return function(node) { var key, newValue, obj, processedKey, ref; - obj = Object.create(null); + obj = {}; obj[charkey] = ""; if (!_this.options.ignoreAttrs) { ref = node.attributes; for (key in ref) { if (!hasProp.call(ref, key)) continue; if (!(attrkey in obj) && !_this.options.mergeAttrs) { - obj[attrkey] = Object.create(null); + obj[attrkey] = {}; } newValue = _this.options.attrValueProcessors ? processItem(_this.options.attrValueProcessors, node.attributes[key], key) : node.attributes[key]; processedKey = _this.options.attrNameProcessors ? processItem(_this.options.attrNameProcessors, key) : key; - if (_this.options.mergeAttrs) { - _this.assignOrPush(obj, processedKey, newValue); - } else { - obj[attrkey][processedKey] = newValue; + if (isValidKey(processedKey)) { + if (_this.options.mergeAttrs) { + _this.assignOrPush(obj, processedKey, newValue); + } else { + obj[attrkey][processedKey] = newValue; + } } } } @@ -226,7 +235,7 @@ } if (_this.options.explicitChildren && !_this.options.mergeAttrs && typeof obj === 'object') { if (!_this.options.preserveChildrenOrder) { - node = Object.create(null); + node = {}; if (_this.options.attrkey in obj) { node[_this.options.attrkey] = obj[_this.options.attrkey]; delete obj[_this.options.attrkey]; @@ -241,10 +250,12 @@ obj = node; } else if (s) { s[_this.options.childkey] = s[_this.options.childkey] || []; - objClone = Object.create(null); + objClone = {}; for (key in obj) { if (!hasProp.call(obj, key)) continue; - objClone[key] = obj[key]; + if (isValidKey(key)) { + objClone[key] = obj[key]; + } } s[_this.options.childkey].push(objClone); delete obj["#name"]; @@ -258,7 +269,7 @@ } else { if (_this.options.explicitRoot) { old = obj; - obj = Object.create(null); + obj = {}; obj[nodeName] = old; } _this.resultObject = obj; diff --git a/src/parser.coffee b/src/parser.coffee index 88ace67d..a0fe9965 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -11,6 +11,9 @@ defaults = require('./defaults').defaults isEmpty = (thing) -> return typeof thing is "object" && thing? && Object.keys(thing).length is 0 +isValidKey = (key) -> + return key != '__proto__' && key != 'constructor' && key != 'prototype' + processItem = (processors, item, key) -> item = process(item, key) for process in processors return item @@ -52,6 +55,7 @@ class exports.Parser extends events @emit err assignOrPush: (obj, key, newValue) => + return if not isValidKey(key) if key not of obj if not @options.explicitArray obj[key] = newValue @@ -102,18 +106,19 @@ class exports.Parser extends events charkey = @options.charkey @saxParser.onopentag = (node) => - obj = Object.create(null) + obj = {} obj[charkey] = "" unless @options.ignoreAttrs for own key of node.attributes if attrkey not of obj and not @options.mergeAttrs - obj[attrkey] = Object.create(null) + obj[attrkey] = {} newValue = if @options.attrValueProcessors then processItem(@options.attrValueProcessors, node.attributes[key], key) else node.attributes[key] processedKey = if @options.attrNameProcessors then processItem(@options.attrNameProcessors, key) else key - if @options.mergeAttrs - @assignOrPush obj, processedKey, newValue - else - obj[attrkey][processedKey] = newValue + if isValidKey(processedKey) + if @options.mergeAttrs + @assignOrPush obj, processedKey, newValue + else + obj[attrkey][processedKey] = newValue # need a place to store the node name obj["#name"] = if @options.tagNameProcessors then processItem(@options.tagNameProcessors, node.name) else node.name @@ -163,7 +168,7 @@ class exports.Parser extends events # put children into property and unfold chars if necessary if @options.explicitChildren and not @options.mergeAttrs and typeof obj is 'object' if not @options.preserveChildrenOrder - node = Object.create(null) + node = {} # separate attributes if @options.attrkey of obj node[@options.attrkey] = obj[@options.attrkey] @@ -181,9 +186,9 @@ class exports.Parser extends events # append current node onto parent's array s[@options.childkey] = s[@options.childkey] or [] # push a clone so that the node in the children array can receive the #name property while the original obj can do without it - objClone = Object.create(null) + objClone = {} for own key of obj - objClone[key] = obj[key] + objClone[key] = obj[key] if isValidKey(key) s[@options.childkey].push objClone delete obj["#name"] # re-check whether we can collapse the node now to just the charkey value @@ -198,7 +203,7 @@ class exports.Parser extends events if @options.explicitRoot # avoid circular references old = obj - obj = Object.create(null) + obj = {} obj[nodeName] = old @resultObject = obj diff --git a/test/parser.test.coffee b/test/parser.test.coffee index 796e2c69..f2758759 100644 --- a/test/parser.test.coffee +++ b/test/parser.test.coffee @@ -547,13 +547,13 @@ module.exports = 'test single attrNameProcessors': skeleton(attrNameProcessors: [nameToUpperCase], (r)-> console.log 'Result object: ' + util.inspect r, false, 10 - equ {}.hasOwnProperty.call(r.sample.attrNameProcessTest[0].$, 'CAMELCASEATTR'), true - equ {}.hasOwnProperty.call(r.sample.attrNameProcessTest[0].$, 'LOWERCASEATTR'), true) + equ r.sample.attrNameProcessTest[0].$.hasOwnProperty('CAMELCASEATTR'), true + equ r.sample.attrNameProcessTest[0].$.hasOwnProperty('LOWERCASEATTR'), true) 'test multiple attrNameProcessors': skeleton(attrNameProcessors: [nameToUpperCase, nameCutoff], (r)-> console.log 'Result object: ' + util.inspect r, false, 10 - equ {}.hasOwnProperty.call(r.sample.attrNameProcessTest[0].$, 'CAME'), true - equ {}.hasOwnProperty.call(r.sample.attrNameProcessTest[0].$, 'LOWE'), true) + equ r.sample.attrNameProcessTest[0].$.hasOwnProperty('CAME'), true + equ r.sample.attrNameProcessTest[0].$.hasOwnProperty('LOWE'), true) 'test single attrValueProcessors': skeleton(attrValueProcessors: [nameToUpperCase], (r)-> console.log 'Result object: ' + util.inspect r, false, 10 @@ -575,21 +575,21 @@ module.exports = 'test single tagNameProcessors': skeleton(tagNameProcessors: [nameToUpperCase], (r)-> console.log 'Result object: ' + util.inspect r, false, 10 - equ {}.hasOwnProperty.call(r, 'SAMPLE'), true - equ {}.hasOwnProperty.call(r.SAMPLE, 'TAGNAMEPROCESSTEST'), true) + equ r.hasOwnProperty('SAMPLE'), true + equ r.SAMPLE.hasOwnProperty('TAGNAMEPROCESSTEST'), true) 'test single tagNameProcessors in simple callback': (test) -> fs.readFile fileName, (err, data) -> xml2js.parseString data, tagNameProcessors: [nameToUpperCase], (err, r)-> console.log 'Result object: ' + util.inspect r, false, 10 - equ {}.hasOwnProperty.call(r, 'SAMPLE'), true - equ {}.hasOwnProperty.call(r.SAMPLE, 'TAGNAMEPROCESSTEST'), true + equ r.hasOwnProperty('SAMPLE'), true + equ r.SAMPLE.hasOwnProperty('TAGNAMEPROCESSTEST'), true test.finish() 'test multiple tagNameProcessors': skeleton(tagNameProcessors: [nameToUpperCase, nameCutoff], (r)-> console.log 'Result object: ' + util.inspect r, false, 10 - equ {}.hasOwnProperty.call(r, 'SAMP'), true - equ {}.hasOwnProperty.call(r.SAMP, 'TAGN'), true) + equ r.hasOwnProperty('SAMP'), true + equ r.SAMP.hasOwnProperty('TAGN'), true) 'test attrValueProcessors key param': skeleton(attrValueProcessors: [replaceValueByName], (r)-> console.log 'Result object: ' + util.inspect r, false, 10