From f73ef826cd29935bc494df6034ecb38b6329075b Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 11 Mar 2019 16:48:55 -0500 Subject: [PATCH 1/3] Support a new option "stopNodes". This field is an array with 0 or more tagnames. When it encounters a tagname that matches the list of stopNodes, it will treat all the stuff understand this tag as a string. This is useful when parsing xml, a part of it is considered as xhtml. --- src/xmlstr2xmlnode.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/xmlstr2xmlnode.js b/src/xmlstr2xmlnode.js index 16122ee6..9b894434 100644 --- a/src/xmlstr2xmlnode.js +++ b/src/xmlstr2xmlnode.js @@ -39,6 +39,7 @@ const defaultOptions = { attrValueProcessor: function(a) { return a; }, + stopNodes: [] //decodeStrict: false, }; @@ -61,6 +62,7 @@ const props = [ 'tagValueProcessor', 'attrValueProcessor', 'parseTrueNumberOnly', + 'stopNodes' ]; exports.props = props; @@ -84,7 +86,11 @@ const getTraversalObj = function(xmlData, options) { if (currentNode.parent && tag[14]) { currentNode.parent.val = util.getValue(currentNode.parent.val) + '' + processTagValue(tag[14], options); } - + if (options.stopNodes && options.stopNodes.includes(currentNode.tagname)) { + currentNode.child = [] + if (currentNode.attrsMap == undefined) { currentNode.attrsMap = {}} + currentNode.val = xmlData.substr(currentNode.startIndex, tag.index - currentNode.startIndex) + } currentNode = currentNode.parent; } else if (tagType === TagType.CDATA) { if (options.cdataTagName) { @@ -119,6 +125,9 @@ const getTraversalObj = function(xmlData, options) { currentNode, processTagValue(tag[14], options) ); + if (options.stopNodes && options.stopNodes.includes(childNode.tagname)) { + childNode.startIndex=tag.index + tag[1].length + } childNode.attrsMap = buildAttributesMap(tag[8], options); currentNode.addChild(childNode); currentNode = childNode; From 4be30be2b46258fcb1539c8dccf731d0ad595831 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Sun, 17 Mar 2019 10:23:09 -0500 Subject: [PATCH 2/3] Add spec (Thanks Amit!) and fixed a bug. --- spec/stopNodes_spec.js | 138 +++++++++++++++++++++++++++++++++++++++++ src/xmlstr2xmlnode.js | 6 +- 2 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 spec/stopNodes_spec.js diff --git a/spec/stopNodes_spec.js b/spec/stopNodes_spec.js new file mode 100644 index 00000000..75b3e76f --- /dev/null +++ b/spec/stopNodes_spec.js @@ -0,0 +1,138 @@ +"use strict"; + +const parser = require("../src/parser"); +const validator = require("../src/validator"); +const he = require("he"); + +describe("XMLParser", function() { + it("1a. should support single stopNode", function() { + const xmlData = `test 1

p 1

div 1
`; + const expected = { + "issue": { + "title": "test 1", + "fix1": "

p 1

div 1
" + } + }; + + let result = parser.parse(xmlData, { + attributeNamePrefix: "", + ignoreAttributes: false, + parseAttributeValue: true, + stopNodes: ["fix1"] + }); + + //console.log(JSON.stringify(result,null,4)); + expect(result).toEqual(expected); + + result = validator.validate(xmlData); + expect(result).toBe(true); + }); + + it("1b. 3. should support more than one stopNodes, with or without attr", function() { + const xmlData = `test 1

p 1

div 1

p 2

div 2
`; + const expected = { + "issue": { + "title": "test 1", + "fix1": { + "#text": "

p 1

div 1
", + "lang": "en" + }, + "fix2": "

p 2

div 2
" + } + }; + + let result = parser.parse(xmlData, { + attributeNamePrefix: "", + ignoreAttributes: false, + parseAttributeValue: true, + stopNodes: ["fix1", "fix2"] + }); + + //console.log(JSON.stringify(result,null,4)); + expect(result).toEqual(expected); + + result = validator.validate(xmlData); + expect(result).toBe(true); + }); + + it("2. stop node has nothing in it", function() { + const xmlData = `test 1`; + const expected = { + "issue": { + "title": "test 1", + "fix1": "" + } + }; + + let result = parser.parse(xmlData, { + attributeNamePrefix: "", + ignoreAttributes: false, + parseAttributeValue: true, + stopNodes: ["fix1", "fix2"] + }); + + //console.log(JSON.stringify(result,null,4)); + expect(result).toEqual(expected); + + result = validator.validate(xmlData); + expect(result).toBe(true); + }); + + it("4. cdata", function() { + const xmlData = ` + + + +122233344550 + Jack]]> + Mohan]]> + + + + + Mohan]]> + +`; + const expected = { + "issue": { + "fix1": "\n +122233344550\n Jack]]>\n Mohan]]>\n \n \n ", + "fix2": "\n\t\tMohan]]>\n\t" + } + }; + + let result = parser.parse(xmlData, { + attributeNamePrefix: "", + ignoreAttributes: false, + parseAttributeValue: true, + stopNodes: ["fix1", "fix2"] + }); + + //console.log(JSON.stringify(result,null,4)); + expect(result).toEqual(expected); + + result = validator.validate(xmlData, { + allowBooleanAttributes: true + }); + expect(result).toBe(true); + }); + + it("5. stopNode at root level", function() { + const xmlData = `

p 1

div 1
`; + const expected = { + "fix1": "

p 1

div 1
" + }; + + let result = parser.parse(xmlData, { + attributeNamePrefix: "", + ignoreAttributes: false, + stopNodes: ["fix1", "fix2"] + }); + + //console.log(JSON.stringify(result,null,4)); + expect(result).toEqual(expected); + + result = validator.validate(xmlData, { + allowBooleanAttributes: true + }); + expect(result).toBe(true); + }); +}); diff --git a/src/xmlstr2xmlnode.js b/src/xmlstr2xmlnode.js index 9b894434..c8e5b83b 100644 --- a/src/xmlstr2xmlnode.js +++ b/src/xmlstr2xmlnode.js @@ -86,10 +86,10 @@ const getTraversalObj = function(xmlData, options) { if (currentNode.parent && tag[14]) { currentNode.parent.val = util.getValue(currentNode.parent.val) + '' + processTagValue(tag[14], options); } - if (options.stopNodes && options.stopNodes.includes(currentNode.tagname)) { + if (options.stopNodes.length && options.stopNodes.includes(currentNode.tagname)) { currentNode.child = [] if (currentNode.attrsMap == undefined) { currentNode.attrsMap = {}} - currentNode.val = xmlData.substr(currentNode.startIndex, tag.index - currentNode.startIndex) + currentNode.val = xmlData.substr(currentNode.startIndex + 1, tag.index - currentNode.startIndex - 1) } currentNode = currentNode.parent; } else if (tagType === TagType.CDATA) { @@ -125,7 +125,7 @@ const getTraversalObj = function(xmlData, options) { currentNode, processTagValue(tag[14], options) ); - if (options.stopNodes && options.stopNodes.includes(childNode.tagname)) { + if (options.stopNodes.length && options.stopNodes.includes(childNode.tagname)) { childNode.startIndex=tag.index + tag[1].length } childNode.attrsMap = buildAttributesMap(tag[8], options); From 7744609726d6e8e6950d3b9f652d365cd4154a81 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Wed, 20 Mar 2019 21:22:04 -0500 Subject: [PATCH 3/3] Add more test cass as Amit suggested. --- spec/stopNodes_spec.js | 51 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/spec/stopNodes_spec.js b/spec/stopNodes_spec.js index 75b3e76f..c36fdf31 100644 --- a/spec/stopNodes_spec.js +++ b/spec/stopNodes_spec.js @@ -55,7 +55,33 @@ describe("XMLParser", function() { expect(result).toBe(true); }); - it("2. stop node has nothing in it", function() { + it("1c. have two stopNodes, one within the other", function() { + const xmlData = `test 1

p 1

p 2

div 2
div 1
`; + const expected = { + "issue": { + "title": "test 1", + "fix1": { + "#text": "

p 1

p 2

div 2
div 1
", + "lang": "en" + } + } + }; + + let result = parser.parse(xmlData, { + attributeNamePrefix: "", + ignoreAttributes: false, + parseAttributeValue: true, + stopNodes: ["fix1", "fix2"] + }); + + //console.log(JSON.stringify(result,null,4)); + expect(result).toEqual(expected); + + result = validator.validate(xmlData); + expect(result).toBe(true); + }); + + it("2a. stop node has nothing in it", function() { const xmlData = `test 1`; const expected = { "issue": { @@ -78,6 +104,29 @@ describe("XMLParser", function() { expect(result).toBe(true); }); + it("2b. stop node is self-closing", function() { + const xmlData = `test 1`; + const expected = { + "issue": { + "title": "test 1", + "fix1": "" + } + }; + + let result = parser.parse(xmlData, { + attributeNamePrefix: "", + ignoreAttributes: false, + parseAttributeValue: true, + stopNodes: ["fix1", "fix2"] + }); + + //console.log(JSON.stringify(result,null,4)); + expect(result).toEqual(expected); + + result = validator.validate(xmlData); + expect(result).toBe(true); + }); + it("4. cdata", function() { const xmlData = `