Skip to content

Commit

Permalink
Support a new option "stopNodes". (#150)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jinq102030 authored and amitguptagwl committed Mar 23, 2019
1 parent 5d424ab commit 03900d9
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 1 deletion.
187 changes: 187 additions & 0 deletions spec/stopNodes_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
"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 = `<issue><title>test 1</title><fix1><p>p 1</p><div class="show">div 1</div></fix1></issue>`;
const expected = {
"issue": {
"title": "test 1",
"fix1": "<p>p 1</p><div class=\"show\">div 1</div>"
}
};

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 = `<issue><title>test 1</title><fix1 lang="en"><p>p 1</p><div class="show">div 1</div></fix1><fix2><p>p 2</p><div class="show">div 2</div></fix2></issue>`;
const expected = {
"issue": {
"title": "test 1",
"fix1": {
"#text": "<p>p 1</p><div class=\"show\">div 1</div>",
"lang": "en"
},
"fix2": "<p>p 2</p><div class=\"show\">div 2</div>"
}
};

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("1c. have two stopNodes, one within the other", function() {
const xmlData = `<issue><title>test 1</title><fix1 lang="en"><p>p 1</p><fix2><p>p 2</p><div class="show">div 2</div></fix2><div class="show">div 1</div></fix1></issue>`;
const expected = {
"issue": {
"title": "test 1",
"fix1": {
"#text": "<p>p 1</p><fix2><p>p 2</p><div class=\"show\">div 2</div></fix2><div class=\"show\">div 1</div>",
"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 = `<issue><title>test 1</title><fix1></fix1></issue>`;
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("2b. stop node is self-closing", function() {
const xmlData = `<issue><title>test 1</title><fix1/></issue>`;
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 = `<?xml version='1.0'?>
<issue>
<fix1>
<phone>+122233344550</phone>
<fix2><![CDATA[<fix1>Jack</fix1>]]><![CDATA[Jack]]></fix2>
<name><![CDATA[<some>Mohan</some>]]></name>
<blank><![CDATA[]]></blank>
<regx><![CDATA[^[ ].*$]]></regx>
</fix1>
<fix2>
<![CDATA[<some>Mohan</some>]]>
</fix2>
</issue>`;
const expected = {
"issue": {
"fix1": "\n <phone>+122233344550</phone>\n <fix2><![CDATA[<fix1>Jack</fix1>]]><![CDATA[Jack]]></fix2>\n <name><![CDATA[<some>Mohan</some>]]></name>\n <blank><![CDATA[]]></blank>\n <regx><![CDATA[^[ ].*$]]></regx>\n ",
"fix2": "\n\t\t<![CDATA[<some>Mohan</some>]]>\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 = `<fix1><p>p 1</p><div class="show">div 1</div></fix1>`;
const expected = {
"fix1": "<p>p 1</p><div class=\"show\">div 1</div>"
};

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);
});
});
11 changes: 10 additions & 1 deletion src/xmlstr2xmlnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const defaultOptions = {
attrValueProcessor: function(a) {
return a;
},
stopNodes: []
//decodeStrict: false,
};

Expand All @@ -61,6 +62,7 @@ const props = [
'tagValueProcessor',
'attrValueProcessor',
'parseTrueNumberOnly',
'stopNodes'
];
exports.props = props;

Expand All @@ -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.length && options.stopNodes.includes(currentNode.tagname)) {
currentNode.child = []
if (currentNode.attrsMap == undefined) { currentNode.attrsMap = {}}
currentNode.val = xmlData.substr(currentNode.startIndex + 1, tag.index - currentNode.startIndex - 1)
}
currentNode = currentNode.parent;
} else if (tagType === TagType.CDATA) {
if (options.cdataTagName) {
Expand Down Expand Up @@ -119,6 +125,9 @@ const getTraversalObj = function(xmlData, options) {
currentNode,
processTagValue(tag[14], options)
);
if (options.stopNodes.length && options.stopNodes.includes(childNode.tagname)) {
childNode.startIndex=tag.index + tag[1].length
}
childNode.attrsMap = buildAttributesMap(tag[8], options);
currentNode.addChild(childNode);
currentNode = childNode;
Expand Down

0 comments on commit 03900d9

Please sign in to comment.