From 88d13cceeb7688135394782eb99d504496a23574 Mon Sep 17 00:00:00 2001 From: varad11 Date: Mon, 22 May 2023 00:43:52 +0530 Subject: [PATCH 1/4] Exception for unopened tags. Fix: Ignore unopened tags and move forward with parser --- spec/xmlParser_spec.js | 32 +++++++++++++++++++++++++++++++ src/xmlparser/OrderedObjParser.js | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/spec/xmlParser_spec.js b/spec/xmlParser_spec.js index ad6f05eb..32f547a9 100644 --- a/spec/xmlParser_spec.js +++ b/spec/xmlParser_spec.js @@ -1066,4 +1066,36 @@ describe("XMLParser", function() { expect(result).toEqual(expected); }); + + it("should ignore closing tags without an opening tag", function() { + const xmlData = ` + + Hello + + + + + `; + const options = { + ignoreAttributes: false, + preserveOrder: false, + alwaysCreateTextNode: false + }; + const expected = { + "rootNode": { + "parentTag": [ + { + "childTag": "Hello", + "@_attr": "my attr" + }, + { + "@_attr": "my attr" + } + ] + } + }; + const parser = new XMLParser(options); + let result = parser.parse(xmlData); + expect(result).toEqual(expected); + }); }); diff --git a/src/xmlparser/OrderedObjParser.js b/src/xmlparser/OrderedObjParser.js index db11a8c0..4e78a128 100644 --- a/src/xmlparser/OrderedObjParser.js +++ b/src/xmlparser/OrderedObjParser.js @@ -220,7 +220,7 @@ const parseXml = function(xmlData) { } jPath = jPath.substring(0, propIndex); - currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope + currentNode = this.tagsNodeStack.length ? this.tagsNodeStack.pop() : currentNode;//avoid recursion, set the parent tag scope textData = ""; i = closeIndex; } else if( xmlData[i+1] === '?') { From 3f4d56c40f656ea240e589a7140560d43b0177eb Mon Sep 17 00:00:00 2001 From: varad11 Date: Tue, 23 May 2023 00:15:33 +0530 Subject: [PATCH 2/4] replaced jPath '.' with '|', ran test cases with new example --- CHANGELOG.md | 3 +++ package.json | 2 +- spec/xmlParser_spec.js | 41 +++++++++++++++++++++++++++++++ src/xmlparser/OrderedObjParser.js | 23 +++++++++-------- 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1f3524a..9e5057a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ Note: If you find missing information about particular minor version, that version must have been changed without any functional change in this library. +**4.2.3 / 2023-05-23** +* fix #573: unopened closing tags should be ignored instead of throwing "*Cannot read properties of undefined*" exception. + **4.2.2 / 2023-04-18** * fix #562: fix unpaired tag when it comes in last of a nested tag. Also throw error when unpaired tag is used as closing tag diff --git a/package.json b/package.json index 90b11f7b..37175c90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fast-xml-parser", - "version": "4.2.2", + "version": "4.2.3", "description": "Validate XML, Parse XML, Build XML without C/C++ based libraries", "main": "./src/fxp.js", "scripts": { diff --git a/spec/xmlParser_spec.js b/spec/xmlParser_spec.js index 32f547a9..2fbe7a28 100644 --- a/spec/xmlParser_spec.js +++ b/spec/xmlParser_spec.js @@ -1098,4 +1098,45 @@ describe("XMLParser", function() { let result = parser.parse(xmlData); expect(result).toEqual(expected); }); + + it("should ignore closing tags without an opening tag", function() { + const xmlData = ` + + Hello + + + + + + + + World + + `; + const options = { + ignoreAttributes: false, + preserveOrder: false, + alwaysCreateTextNode: false + }; + const expected = { + "rootNode": { + "parentTag": [ + { + "childTag": "Hello", + "@_attr": "my attr" + }, + { + "@_attr": "my attr" + }, + { + "childTag": "World", + "@_attr": "my attr" + } + ] + } + }; + const parser = new XMLParser(options); + let result = parser.parse(xmlData); + expect(result).toEqual(expected); + }); }); diff --git a/src/xmlparser/OrderedObjParser.js b/src/xmlparser/OrderedObjParser.js index 4e78a128..cc4af585 100644 --- a/src/xmlparser/OrderedObjParser.js +++ b/src/xmlparser/OrderedObjParser.js @@ -207,20 +207,23 @@ const parseXml = function(xmlData) { } //check if last tag of nested tag was unpaired tag - const lastTagName = jPath.substring(jPath.lastIndexOf(".")+1); + const lastTagName = jPath.substring(jPath.lastIndexOf("|")+1); if(tagName && this.options.unpairedTags.indexOf(tagName) !== -1 ){ throw new Error(`Unpaired tag can not be used as closing tag: `); } let propIndex = 0 if(lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1 ){ - propIndex = jPath.lastIndexOf('.', jPath.lastIndexOf('.')-1) + propIndex = jPath.lastIndexOf('|', jPath.lastIndexOf('|')-1) this.tagsNodeStack.pop(); }else{ - propIndex = jPath.lastIndexOf("."); - } - jPath = jPath.substring(0, propIndex); + propIndex = jPath.lastIndexOf("|"); + } + if(lastTagName === tagName) { + //update path and pop out node only when the closing tag matches with opening tag. This condition will thus ignore unopened closing tags. + jPath = jPath.substring(0, propIndex); - currentNode = this.tagsNodeStack.length ? this.tagsNodeStack.pop() : currentNode;//avoid recursion, set the parent tag scope + currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope + } textData = ""; i = closeIndex; } else if( xmlData[i+1] === '?') { @@ -300,10 +303,10 @@ const parseXml = function(xmlData) { const lastTag = currentNode; if(lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1 ){ currentNode = this.tagsNodeStack.pop(); - jPath = jPath.substring(0, jPath.lastIndexOf(".")); + jPath = jPath.substring(0, jPath.lastIndexOf("|")); } if(tagName !== xmlObj.tagname){ - jPath += jPath ? "." + tagName : tagName; + jPath += jPath ? "|" + tagName : tagName; } if (this.isItStopNode(this.options.stopNodes, jPath, tagName)) { //TODO: namespace let tagContent = ""; @@ -332,7 +335,7 @@ const parseXml = function(xmlData) { tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true); } - jPath = jPath.substr(0, jPath.lastIndexOf(".")); + jPath = jPath.substr(0, jPath.lastIndexOf("|")); childNode.add(this.options.textNodeName, tagContent); this.addChild(currentNode, childNode, jPath) @@ -355,7 +358,7 @@ const parseXml = function(xmlData) { childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName); } this.addChild(currentNode, childNode, jPath) - jPath = jPath.substr(0, jPath.lastIndexOf(".")); + jPath = jPath.substr(0, jPath.lastIndexOf("|")); } //opening tag else{ From 18e252e00158c55a4107c8d93febb33cd40dfad4 Mon Sep 17 00:00:00 2001 From: varad11 Date: Tue, 23 May 2023 01:17:40 +0530 Subject: [PATCH 3/4] updated test spec --- spec/xmlParser_spec.js | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/spec/xmlParser_spec.js b/spec/xmlParser_spec.js index 2fbe7a28..730501ab 100644 --- a/spec/xmlParser_spec.js +++ b/spec/xmlParser_spec.js @@ -1067,38 +1067,6 @@ describe("XMLParser", function() { expect(result).toEqual(expected); }); - it("should ignore closing tags without an opening tag", function() { - const xmlData = ` - - Hello - - - - - `; - const options = { - ignoreAttributes: false, - preserveOrder: false, - alwaysCreateTextNode: false - }; - const expected = { - "rootNode": { - "parentTag": [ - { - "childTag": "Hello", - "@_attr": "my attr" - }, - { - "@_attr": "my attr" - } - ] - } - }; - const parser = new XMLParser(options); - let result = parser.parse(xmlData); - expect(result).toEqual(expected); - }); - it("should ignore closing tags without an opening tag", function() { const xmlData = ` From 456dab1940815a5cf52268b4f47a38bc8ba93110 Mon Sep 17 00:00:00 2001 From: varad11 Date: Tue, 23 May 2023 01:22:00 +0530 Subject: [PATCH 4/4] modified changelog.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e5057a6..ddaf6b1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Note: If you find missing information about particular minor version, that version must have been changed without any functional change in this library. **4.2.3 / 2023-05-23** -* fix #573: unopened closing tags should be ignored instead of throwing "*Cannot read properties of undefined*" exception. +* fix: Unopened closing tags should be ignored by XMLParser instead of throwing "*Cannot read properties of undefined*" exception. **4.2.2 / 2023-04-18** * fix #562: fix unpaired tag when it comes in last of a nested tag. Also throw error when unpaired tag is used as closing tag