Skip to content

Commit 652a29e

Browse files
committedApr 9, 2023
support updateTag
1 parent 5d4d846 commit 652a29e

File tree

5 files changed

+211
-109
lines changed

5 files changed

+211
-109
lines changed
 

‎spec/attr_spec.js

+1-100
Original file line numberDiff line numberDiff line change
@@ -359,104 +359,5 @@ id="7" data="foo bar" bug="true"/>`;
359359
// console.log(output);
360360
expect(output.replace(/\s+/g, "")).toEqual(XMLdata.replace(/\s+/g, ""));
361361
});
362-
it("should parse attributes with valid names", function() {
363-
const xmlData = `<root>
364-
<a keep="me" skip="me"></a>
365-
<a skip="me"></a>
366-
<a need="friend"></a>
367-
<a camel="case" MakeMe="lower"></a>
368-
<b change="val"></b>
369-
</root>`;
370-
const expected = {
371-
"root": {
372-
"a": [
373-
{
374-
"keep": "me"
375-
},
376-
"",
377-
{
378-
"need": "friend",
379-
"friend": "me"
380-
},
381-
{
382-
"Camel": "case",
383-
"makeme": "lower"
384-
}
385-
],
386-
"b": {
387-
"change": "VAL"
388-
}
389-
}
390-
};
391-
const options = {
392-
attributeNamePrefix: "",
393-
ignoreAttributes: false,
394-
parseAttributeValue: true,
395-
updateAttributes(tagName, attrs, jPath){
396-
if(attrs["skip"]) delete attrs["skip"]
397-
if(attrs["camel"]) {
398-
attrs["Camel"] = attrs["camel"];
399-
delete attrs["camel"];
400-
}
401-
if(attrs["need"]) {
402-
attrs["friend"] = "me";
403-
}
404-
if(attrs["MakeMe"]) {
405-
attrs["makeme"] = attrs["MakeMe"];
406-
delete attrs["MakeMe"];
407-
}
408-
if(attrs["change"]) {
409-
attrs["change"] = attrs["change"].toUpperCase();
410-
}
411-
return attrs;
412-
}
413-
};
414-
415-
const parser = new XMLParser(options);
416-
let result = parser.parse(xmlData);
417-
418-
// console.log(JSON.stringify(result,null,4));
419-
expect(result).toEqual(expected);
420-
421-
result = XMLValidator.validate(xmlData);
422-
expect(result).toBe(true);
423-
});
424-
it("should parse attributes with valid names", function() {
425-
const xmlData = `<root>
426-
<a keep="me" skip="me"></a>
427-
<a skip="me"></a>
428-
<a need="friend"></a>
429-
<a camel="case" MakeMe="lower"></a>
430-
<b change="val"></b>
431-
</root>`;
432-
const expected = {
433-
"root": {
434-
"a": [
435-
"",
436-
"",
437-
"",
438-
""
439-
],
440-
"b": ""
441-
}
442-
};
443-
const options = {
444-
attributeNamePrefix: "",
445-
ignoreAttributes: false,
446-
parseAttributeValue: true,
447-
updateAttributes(tagName, attrs,jPath){
448-
// console.log("called")
449-
return null;
450-
}
451-
};
452-
453-
const parser = new XMLParser(options);
454-
let result = parser.parse(xmlData);
455-
456-
// console.log(JSON.stringify(result,null,4));
457-
expect(result).toEqual(expected);
458-
459-
result = XMLValidator.validate(xmlData);
460-
expect(result).toBe(true);
461-
});
462362
});
363+

‎spec/updateTag_spec.js

+182
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
"use strict";
2+
3+
const {XMLParser, XMLBuilder, XMLValidator} = require("../src/fxp");
4+
const he = require("he");
5+
6+
describe("XMLParser updateTag ", function() {
7+
it("should delete, join, update attribute name and value", function() {
8+
const xmlData = `<root>
9+
<a keep="me" skip="me"></a>
10+
<a skip="me"></a>
11+
<a need="friend"></a>
12+
<a camel="case" MakeMe="lower"></a>
13+
<b change="val"></b>
14+
</root>`;
15+
const expected = {
16+
"root": {
17+
"a": [
18+
{
19+
"keep": "me"
20+
},
21+
"",
22+
{
23+
"need": "friend",
24+
"friend": "me"
25+
},
26+
{
27+
"Camel": "case",
28+
"makeme": "lower"
29+
}
30+
],
31+
"b": {
32+
"change": "VAL"
33+
}
34+
}
35+
};
36+
const options = {
37+
attributeNamePrefix: "",
38+
ignoreAttributes: false,
39+
parseAttributeValue: true,
40+
updateTag(tagName, jPath, attrs){
41+
if(attrs["skip"]) delete attrs["skip"]
42+
if(attrs["camel"]) {
43+
attrs["Camel"] = attrs["camel"];
44+
delete attrs["camel"];
45+
}
46+
if(attrs["need"]) {
47+
attrs["friend"] = "me";
48+
}
49+
if(attrs["MakeMe"]) {
50+
attrs["makeme"] = attrs["MakeMe"];
51+
delete attrs["MakeMe"];
52+
}
53+
if(attrs["change"]) {
54+
attrs["change"] = attrs["change"].toUpperCase();
55+
}
56+
return tagName;
57+
}
58+
};
59+
60+
const parser = new XMLParser(options);
61+
let result = parser.parse(xmlData);
62+
63+
// console.log(JSON.stringify(result,null,4));
64+
expect(result).toEqual(expected);
65+
66+
result = XMLValidator.validate(xmlData);
67+
expect(result).toBe(true);
68+
});
69+
it("should delete all the attributes", function() {
70+
const xmlData = `<root>
71+
<a keep="me" skip="me"></a>
72+
<a skip="me"></a>
73+
<a need="friend"></a>
74+
<a camel="case" MakeMe="lower"></a>
75+
<b change="val"></b>
76+
</root>`;
77+
const expected = {
78+
"root": {
79+
"a": [
80+
"",
81+
"",
82+
"",
83+
""
84+
],
85+
"b": ""
86+
}
87+
};
88+
const options = {
89+
attributeNamePrefix: "",
90+
ignoreAttributes: false,
91+
parseAttributeValue: true,
92+
updateTag(tagName, jPath, attrs){
93+
for (var k in attrs){
94+
if (attrs.hasOwnProperty(k)){
95+
delete attrs[k];
96+
}
97+
}
98+
return tagName;
99+
}
100+
};
101+
102+
const parser = new XMLParser(options);
103+
let result = parser.parse(xmlData);
104+
105+
// console.log(JSON.stringify(result,null,4));
106+
expect(result).toEqual(expected);
107+
108+
result = XMLValidator.validate(xmlData);
109+
expect(result).toBe(true);
110+
});
111+
it("should skip a tag or modify tag name", function() {
112+
const xmlData = `<html>
113+
<header></header>
114+
<body>
115+
<h1 class="highlight" >Post title</h1>
116+
<content>
117+
<img width="200" height="500">
118+
<p>some text</p>
119+
<img width="200" height="200">
120+
<p join="a" all="b" in="c" one="d" >some text 2</p>
121+
<img width="500" height="500">
122+
</content>
123+
<script></script>
124+
</body>
125+
</html>`;
126+
const expected = {
127+
"html": {
128+
"header": "",
129+
"body": {
130+
"h1": {
131+
"#text": "Post title",
132+
"class": "highlight underline"
133+
},
134+
"div": {
135+
"p": [
136+
"some text",
137+
{
138+
"#text": "some text 2",
139+
"joint": "abcd"
140+
}
141+
],
142+
"img": {
143+
"width": "200",
144+
"height": "200"
145+
}
146+
}
147+
}
148+
}
149+
};
150+
const options = {
151+
ignoreAttributes: false,
152+
attributeNamePrefix: "",
153+
updateTag: function(tagname, jPath, attrs){
154+
155+
if(tagname ==="h1" && attrs["class"] && attrs["class"].indexOf("highlight") > -1){
156+
attrs["class"] += " underline"
157+
}else if(attrs["join"]){
158+
let val = "";
159+
Object.keys(attrs).forEach( a => {
160+
val+= attrs[a]
161+
delete attrs[a];
162+
});
163+
attrs["joint"] = val;
164+
}
165+
if(tagname === "script") return false;
166+
else if(tagname === "img"){
167+
if(attrs.width > 200 || attrs.height > 200) return false;
168+
}else if(tagname === "content"){
169+
return "div"
170+
}
171+
return tagname;
172+
},
173+
unpairedTags: ["img"]
174+
};
175+
const parser = new XMLParser(options);
176+
let result = parser.parse(xmlData);
177+
178+
// console.log(JSON.stringify(result,null,4));
179+
expect(result).toEqual(expected);
180+
181+
});
182+
});

‎src/fxp.d.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,14 @@ Control how tag value should be parsed. Called only if tag value is not empty
3232
ignorePiTags: boolean;
3333
transformTagName: ((tagName: string) => string) | false;
3434
transformAttributeName: ((attributeName: string) => string) | false;
35-
updateAttributes(tagName: string, jPath: string, attrs: {[k: string]: string}): {[k: string]: string};
35+
/**
36+
Change the tag name when a different name is returned. Skip the tag from parsed result when false is returned.
37+
Modify `attrs` object to control attributes for the given tag.
38+
39+
@returns {string} new tag name.
40+
@returns false to skip the tag
41+
*/
42+
updateTag: (tagName: string, jPath: string, attrs: {[k: string]: string}) => string | boolean;
3643
};
3744
type strnumOptions = {
3845
hex: boolean;

‎src/xmlparser/OptionsBuilder.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ const defaultOptions = {
3434
ignorePiTags: false,
3535
transformTagName: false,
3636
transformAttributeName: false,
37-
updateAttributes: function(tagName, attrs, jPath){
38-
return attrs;
37+
updateTag: function(tagName, jPath, attrs){
38+
return tagName
3939
}
4040
};
4141

‎src/xmlparser/OrderedObjParser.js

+18-6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class OrderedObjParser{
5050
this.replaceEntitiesValue = replaceEntitiesValue;
5151
this.readStopNodeData = readStopNodeData;
5252
this.saveTextToParentTag = saveTextToParentTag;
53+
this.addChild = addChild;
5354
}
5455

5556
}
@@ -171,7 +172,7 @@ function buildAttributesMap(attrStr, jPath, tagName) {
171172
attrCollection[this.options.attributesGroupName] = attrs;
172173
return attrCollection;
173174
}
174-
return this.options.updateAttributes(tagName, attrs, jPath)
175+
return attrs
175176
}
176177
}
177178

@@ -226,7 +227,7 @@ const parseXml = function(xmlData) {
226227
if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
227228
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
228229
}
229-
currentNode.addChild(childNode);
230+
this.addChild(currentNode, childNode, jPath)
230231

231232
}
232233

@@ -323,7 +324,7 @@ const parseXml = function(xmlData) {
323324
jPath = jPath.substr(0, jPath.lastIndexOf("."));
324325
childNode.add(this.options.textNodeName, tagContent);
325326

326-
currentNode.addChild(childNode);
327+
this.addChild(currentNode, childNode, jPath)
327328
}else{
328329
//selfClosing tag
329330
if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
@@ -343,7 +344,7 @@ const parseXml = function(xmlData) {
343344
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
344345
}
345346
jPath = jPath.substr(0, jPath.lastIndexOf("."));
346-
currentNode.addChild(childNode);
347+
this.addChild(currentNode, childNode, jPath)
347348
}
348349
//opening tag
349350
else{
@@ -353,7 +354,7 @@ const parseXml = function(xmlData) {
353354
if(tagName !== tagExp && attrExpPresent){
354355
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
355356
}
356-
currentNode.addChild(childNode);
357+
this.addChild(currentNode, childNode, jPath)
357358
currentNode = childNode;
358359
}
359360
textData = "";
@@ -367,6 +368,17 @@ const parseXml = function(xmlData) {
367368
return xmlObj.child;
368369
}
369370

371+
function addChild(currentNode, childNode, jPath){
372+
const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"])
373+
if(result === false){
374+
}else if(typeof result === "string"){
375+
childNode.tagname = result
376+
currentNode.addChild(childNode);
377+
}else{
378+
currentNode.addChild(childNode);
379+
}
380+
}
381+
370382
const replaceEntitiesValue = function(val){
371383

372384
if(this.options.processEntities){
@@ -423,7 +435,7 @@ function isItStopNode(stopNodes, jPath, currentTagName){
423435
}
424436

425437
/**
426-
* Returns the tag Expression and where it is ending handling single-dobule quotes situation
438+
* Returns the tag Expression and where it is ending handling single-double quotes situation
427439
* @param {string} xmlData
428440
* @param {number} i starting index
429441
* @returns

0 commit comments

Comments
 (0)
Please sign in to comment.