Skip to content

Commit 03900d9

Browse files
jinq102030amitguptagwl
authored andcommitted
Support a new option "stopNodes". (#150)
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.
1 parent 5d424ab commit 03900d9

File tree

2 files changed

+197
-1
lines changed

2 files changed

+197
-1
lines changed

spec/stopNodes_spec.js

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
"use strict";
2+
3+
const parser = require("../src/parser");
4+
const validator = require("../src/validator");
5+
const he = require("he");
6+
7+
describe("XMLParser", function() {
8+
it("1a. should support single stopNode", function() {
9+
const xmlData = `<issue><title>test 1</title><fix1><p>p 1</p><div class="show">div 1</div></fix1></issue>`;
10+
const expected = {
11+
"issue": {
12+
"title": "test 1",
13+
"fix1": "<p>p 1</p><div class=\"show\">div 1</div>"
14+
}
15+
};
16+
17+
let result = parser.parse(xmlData, {
18+
attributeNamePrefix: "",
19+
ignoreAttributes: false,
20+
parseAttributeValue: true,
21+
stopNodes: ["fix1"]
22+
});
23+
24+
//console.log(JSON.stringify(result,null,4));
25+
expect(result).toEqual(expected);
26+
27+
result = validator.validate(xmlData);
28+
expect(result).toBe(true);
29+
});
30+
31+
it("1b. 3. should support more than one stopNodes, with or without attr", function() {
32+
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>`;
33+
const expected = {
34+
"issue": {
35+
"title": "test 1",
36+
"fix1": {
37+
"#text": "<p>p 1</p><div class=\"show\">div 1</div>",
38+
"lang": "en"
39+
},
40+
"fix2": "<p>p 2</p><div class=\"show\">div 2</div>"
41+
}
42+
};
43+
44+
let result = parser.parse(xmlData, {
45+
attributeNamePrefix: "",
46+
ignoreAttributes: false,
47+
parseAttributeValue: true,
48+
stopNodes: ["fix1", "fix2"]
49+
});
50+
51+
//console.log(JSON.stringify(result,null,4));
52+
expect(result).toEqual(expected);
53+
54+
result = validator.validate(xmlData);
55+
expect(result).toBe(true);
56+
});
57+
58+
it("1c. have two stopNodes, one within the other", function() {
59+
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>`;
60+
const expected = {
61+
"issue": {
62+
"title": "test 1",
63+
"fix1": {
64+
"#text": "<p>p 1</p><fix2><p>p 2</p><div class=\"show\">div 2</div></fix2><div class=\"show\">div 1</div>",
65+
"lang": "en"
66+
}
67+
}
68+
};
69+
70+
let result = parser.parse(xmlData, {
71+
attributeNamePrefix: "",
72+
ignoreAttributes: false,
73+
parseAttributeValue: true,
74+
stopNodes: ["fix1", "fix2"]
75+
});
76+
77+
//console.log(JSON.stringify(result,null,4));
78+
expect(result).toEqual(expected);
79+
80+
result = validator.validate(xmlData);
81+
expect(result).toBe(true);
82+
});
83+
84+
it("2a. stop node has nothing in it", function() {
85+
const xmlData = `<issue><title>test 1</title><fix1></fix1></issue>`;
86+
const expected = {
87+
"issue": {
88+
"title": "test 1",
89+
"fix1": ""
90+
}
91+
};
92+
93+
let result = parser.parse(xmlData, {
94+
attributeNamePrefix: "",
95+
ignoreAttributes: false,
96+
parseAttributeValue: true,
97+
stopNodes: ["fix1", "fix2"]
98+
});
99+
100+
//console.log(JSON.stringify(result,null,4));
101+
expect(result).toEqual(expected);
102+
103+
result = validator.validate(xmlData);
104+
expect(result).toBe(true);
105+
});
106+
107+
it("2b. stop node is self-closing", function() {
108+
const xmlData = `<issue><title>test 1</title><fix1/></issue>`;
109+
const expected = {
110+
"issue": {
111+
"title": "test 1",
112+
"fix1": ""
113+
}
114+
};
115+
116+
let result = parser.parse(xmlData, {
117+
attributeNamePrefix: "",
118+
ignoreAttributes: false,
119+
parseAttributeValue: true,
120+
stopNodes: ["fix1", "fix2"]
121+
});
122+
123+
//console.log(JSON.stringify(result,null,4));
124+
expect(result).toEqual(expected);
125+
126+
result = validator.validate(xmlData);
127+
expect(result).toBe(true);
128+
});
129+
130+
it("4. cdata", function() {
131+
const xmlData = `<?xml version='1.0'?>
132+
<issue>
133+
<fix1>
134+
<phone>+122233344550</phone>
135+
<fix2><![CDATA[<fix1>Jack</fix1>]]><![CDATA[Jack]]></fix2>
136+
<name><![CDATA[<some>Mohan</some>]]></name>
137+
<blank><![CDATA[]]></blank>
138+
<regx><![CDATA[^[ ].*$]]></regx>
139+
</fix1>
140+
<fix2>
141+
<![CDATA[<some>Mohan</some>]]>
142+
</fix2>
143+
</issue>`;
144+
const expected = {
145+
"issue": {
146+
"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 ",
147+
"fix2": "\n\t\t<![CDATA[<some>Mohan</some>]]>\n\t"
148+
}
149+
};
150+
151+
let result = parser.parse(xmlData, {
152+
attributeNamePrefix: "",
153+
ignoreAttributes: false,
154+
parseAttributeValue: true,
155+
stopNodes: ["fix1", "fix2"]
156+
});
157+
158+
//console.log(JSON.stringify(result,null,4));
159+
expect(result).toEqual(expected);
160+
161+
result = validator.validate(xmlData, {
162+
allowBooleanAttributes: true
163+
});
164+
expect(result).toBe(true);
165+
});
166+
167+
it("5. stopNode at root level", function() {
168+
const xmlData = `<fix1><p>p 1</p><div class="show">div 1</div></fix1>`;
169+
const expected = {
170+
"fix1": "<p>p 1</p><div class=\"show\">div 1</div>"
171+
};
172+
173+
let result = parser.parse(xmlData, {
174+
attributeNamePrefix: "",
175+
ignoreAttributes: false,
176+
stopNodes: ["fix1", "fix2"]
177+
});
178+
179+
//console.log(JSON.stringify(result,null,4));
180+
expect(result).toEqual(expected);
181+
182+
result = validator.validate(xmlData, {
183+
allowBooleanAttributes: true
184+
});
185+
expect(result).toBe(true);
186+
});
187+
});

src/xmlstr2xmlnode.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const defaultOptions = {
3939
attrValueProcessor: function(a) {
4040
return a;
4141
},
42+
stopNodes: []
4243
//decodeStrict: false,
4344
};
4445

@@ -61,6 +62,7 @@ const props = [
6162
'tagValueProcessor',
6263
'attrValueProcessor',
6364
'parseTrueNumberOnly',
65+
'stopNodes'
6466
];
6567
exports.props = props;
6668

@@ -84,7 +86,11 @@ const getTraversalObj = function(xmlData, options) {
8486
if (currentNode.parent && tag[14]) {
8587
currentNode.parent.val = util.getValue(currentNode.parent.val) + '' + processTagValue(tag[14], options);
8688
}
87-
89+
if (options.stopNodes.length && options.stopNodes.includes(currentNode.tagname)) {
90+
currentNode.child = []
91+
if (currentNode.attrsMap == undefined) { currentNode.attrsMap = {}}
92+
currentNode.val = xmlData.substr(currentNode.startIndex + 1, tag.index - currentNode.startIndex - 1)
93+
}
8894
currentNode = currentNode.parent;
8995
} else if (tagType === TagType.CDATA) {
9096
if (options.cdataTagName) {
@@ -119,6 +125,9 @@ const getTraversalObj = function(xmlData, options) {
119125
currentNode,
120126
processTagValue(tag[14], options)
121127
);
128+
if (options.stopNodes.length && options.stopNodes.includes(childNode.tagname)) {
129+
childNode.startIndex=tag.index + tag[1].length
130+
}
122131
childNode.attrsMap = buildAttributesMap(tag[8], options);
123132
currentNode.addChild(childNode);
124133
currentNode = childNode;

0 commit comments

Comments
 (0)