From 19a4c2fbcd4353cb9017b8f015b6ae0f52913c77 Mon Sep 17 00:00:00 2001 From: Jan Stepanek Date: Tue, 21 May 2019 19:01:35 +0200 Subject: [PATCH 1/3] Call function for emptyTag if specified In case there are multiple empty tags in xml, they all share reference to the same default object. This causes unwanted duplication, when we assign something to any of these empty objects. When we provide factory function for emptyObject, these objects no longer share reference and works as expected. --- lib/parser.js | 4 ++-- src/parser.coffee | 2 +- test/fixtures/sample.xml | 3 +++ test/parser.test.coffee | 5 +++++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/parser.js b/lib/parser.js index 9e8261eb..bed4910f 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -170,7 +170,7 @@ })(this); this.saxParser.onclosetag = (function(_this) { return function() { - var cdata, emptyStr, key, node, nodeName, obj, objClone, old, s, xpath; + var base, cdata, emptyStr, key, node, nodeName, obj, objClone, old, ref, s, xpath; obj = stack.pop(); nodeName = obj["#name"]; if (!_this.options.explicitChildren || !_this.options.preserveChildrenOrder) { @@ -197,7 +197,7 @@ } } if (isEmpty(obj)) { - obj = _this.options.emptyTag !== '' ? _this.options.emptyTag : emptyStr; + obj = (ref = typeof (base = _this.options).emptyTag === "function" ? base.emptyTag() : void 0) != null ? ref : (_this.options.emptyTag !== '' ? _this.options.emptyTag : emptyStr); } if (_this.options.validator != null) { xpath = "/" + ((function() { diff --git a/src/parser.coffee b/src/parser.coffee index 8a375197..ff640a1e 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -145,7 +145,7 @@ class exports.Parser extends events.EventEmitter obj = obj[charkey] if (isEmpty obj) - obj = if @options.emptyTag != '' then @options.emptyTag else emptyStr + obj = @options.emptyTag?() ? (if @options.emptyTag != '' then @options.emptyTag else emptyStr) if @options.validator? xpath = "/" + (node["#name"] for node in stack).concat(nodeName).join("/") diff --git a/test/fixtures/sample.xml b/test/fixtures/sample.xml index 0ffea408..e25a6feb 100644 --- a/test/fixtures/sample.xml +++ b/test/fixtures/sample.xml @@ -55,4 +55,7 @@ some value this is text with markup like this in the middle + + + diff --git a/test/parser.test.coffee b/test/parser.test.coffee index 6d531d56..cf3798c1 100644 --- a/test/parser.test.coffee +++ b/test/parser.test.coffee @@ -72,6 +72,11 @@ module.exports = # determine number of items in object equ Object.keys(r.sample.tagcasetest[0]).length, 3) + 'test parse with empty objects and functions': skeleton({emptyTag: ()=> new Object()}, (r)-> + console.log 'Result object: ' + util.inspect r, false, 10 + bool = r.sample.emptytestanother[0] is r.sample.emptytest[0] + equ bool, false) + 'test parse with explicitCharkey': skeleton(explicitCharkey: true, (r) -> console.log 'Result object: ' + util.inspect r, false, 10 equ r.sample.chartest[0].$.desc, 'Test for CHARs' From f074644bc32ae6f3012962686ad70188bf98f61a Mon Sep 17 00:00:00 2001 From: Jan Stepanek Date: Wed, 22 May 2019 09:05:36 +0200 Subject: [PATCH 2/3] cr fixes (will be squashed after another cr) --- lib/parser.js | 8 ++++++-- src/parser.coffee | 5 ++++- test/parser.test.coffee | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/parser.js b/lib/parser.js index bed4910f..2b8f88eb 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -170,7 +170,7 @@ })(this); this.saxParser.onclosetag = (function(_this) { return function() { - var base, cdata, emptyStr, key, node, nodeName, obj, objClone, old, ref, s, xpath; + var cdata, emptyStr, key, node, nodeName, obj, objClone, old, s, xpath; obj = stack.pop(); nodeName = obj["#name"]; if (!_this.options.explicitChildren || !_this.options.preserveChildrenOrder) { @@ -197,7 +197,11 @@ } } if (isEmpty(obj)) { - obj = (ref = typeof (base = _this.options).emptyTag === "function" ? base.emptyTag() : void 0) != null ? ref : (_this.options.emptyTag !== '' ? _this.options.emptyTag : emptyStr); + if (typeof _this.options.emptyTag === 'function') { + obj = _this.options.emptyTag(); + } else { + obj = _this.options.emptyTag !== '' ? _this.options.emptyTag : emptyStr; + } } if (_this.options.validator != null) { xpath = "/" + ((function() { diff --git a/src/parser.coffee b/src/parser.coffee index ff640a1e..5ff379fa 100644 --- a/src/parser.coffee +++ b/src/parser.coffee @@ -145,7 +145,10 @@ class exports.Parser extends events.EventEmitter obj = obj[charkey] if (isEmpty obj) - obj = @options.emptyTag?() ? (if @options.emptyTag != '' then @options.emptyTag else emptyStr) + if typeof @options.emptyTag == 'function' + obj = @options.emptyTag() + else + obj = if @options.emptyTag != '' then @options.emptyTag else emptyStr if @options.validator? xpath = "/" + (node["#name"] for node in stack).concat(nodeName).join("/") diff --git a/test/parser.test.coffee b/test/parser.test.coffee index cf3798c1..354bd61f 100644 --- a/test/parser.test.coffee +++ b/test/parser.test.coffee @@ -72,7 +72,7 @@ module.exports = # determine number of items in object equ Object.keys(r.sample.tagcasetest[0]).length, 3) - 'test parse with empty objects and functions': skeleton({emptyTag: ()=> new Object()}, (r)-> + 'test parse with empty objects and functions': skeleton({emptyTag: ()=> ({})}, (r)-> console.log 'Result object: ' + util.inspect r, false, 10 bool = r.sample.emptytestanother[0] is r.sample.emptytest[0] equ bool, false) From fa32064c935a3b2b7bc05eab7599c769994c7a0c Mon Sep 17 00:00:00 2001 From: Jan Stepanek Date: Sat, 22 Jun 2019 23:13:00 +0200 Subject: [PATCH 3/3] readme updated with default empty tag as function --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c3278f7e..697b4604 100644 --- a/README.md +++ b/README.md @@ -289,7 +289,10 @@ value})``. Possible options are: * `normalize` (default: `false`): Trim whitespaces inside text nodes. * `explicitRoot` (default: `true`): Set this if you want to get the root node in the resulting object. - * `emptyTag` (default: `''`): what will the value of empty nodes be. + * `emptyTag` (default: `''`): what will the value of empty nodes be. In case + you want to use an empty object as a default value, it is better to provide a factory + function `() => ({})` instead. Without this function a plain object would + become a shared reference across all occurrences with unwanted behavior. * `explicitArray` (default: `true`): Always put child nodes in an array if true; otherwise an array is created only if there is more than one. * `ignoreAttrs` (default: `false`): Ignore all XML attributes and only create