diff --git a/spec/attr_spec.js b/spec/attr_spec.js
index 0d8b1e9a..f8bffe2e 100644
--- a/spec/attr_spec.js
+++ b/spec/attr_spec.js
@@ -359,104 +359,5 @@ id="7" data="foo bar" bug="true"/>`;
// console.log(output);
expect(output.replace(/\s+/g, "")).toEqual(XMLdata.replace(/\s+/g, ""));
});
- it("should parse attributes with valid names", function() {
- const xmlData = `
-
-
-
-
-
- `;
- const expected = {
- "root": {
- "a": [
- {
- "keep": "me"
- },
- "",
- {
- "need": "friend",
- "friend": "me"
- },
- {
- "Camel": "case",
- "makeme": "lower"
- }
- ],
- "b": {
- "change": "VAL"
- }
- }
- };
- const options = {
- attributeNamePrefix: "",
- ignoreAttributes: false,
- parseAttributeValue: true,
- updateAttributes(tagName, attrs, jPath){
- if(attrs["skip"]) delete attrs["skip"]
- if(attrs["camel"]) {
- attrs["Camel"] = attrs["camel"];
- delete attrs["camel"];
- }
- if(attrs["need"]) {
- attrs["friend"] = "me";
- }
- if(attrs["MakeMe"]) {
- attrs["makeme"] = attrs["MakeMe"];
- delete attrs["MakeMe"];
- }
- if(attrs["change"]) {
- attrs["change"] = attrs["change"].toUpperCase();
- }
- return attrs;
- }
- };
-
- const parser = new XMLParser(options);
- let result = parser.parse(xmlData);
-
- // console.log(JSON.stringify(result,null,4));
- expect(result).toEqual(expected);
-
- result = XMLValidator.validate(xmlData);
- expect(result).toBe(true);
- });
- it("should parse attributes with valid names", function() {
- const xmlData = `
-
-
-
-
-
- `;
- const expected = {
- "root": {
- "a": [
- "",
- "",
- "",
- ""
- ],
- "b": ""
- }
- };
- const options = {
- attributeNamePrefix: "",
- ignoreAttributes: false,
- parseAttributeValue: true,
- updateAttributes(tagName, attrs,jPath){
- // console.log("called")
- return null;
- }
- };
-
- const parser = new XMLParser(options);
- let result = parser.parse(xmlData);
-
- // console.log(JSON.stringify(result,null,4));
- expect(result).toEqual(expected);
-
- result = XMLValidator.validate(xmlData);
- expect(result).toBe(true);
- });
});
+
\ No newline at end of file
diff --git a/spec/updateTag_spec.js b/spec/updateTag_spec.js
new file mode 100644
index 00000000..dbfded3e
--- /dev/null
+++ b/spec/updateTag_spec.js
@@ -0,0 +1,182 @@
+"use strict";
+
+const {XMLParser, XMLBuilder, XMLValidator} = require("../src/fxp");
+const he = require("he");
+
+describe("XMLParser updateTag ", function() {
+ it("should delete, join, update attribute name and value", function() {
+ const xmlData = `
+
+
+
+
+
+ `;
+ const expected = {
+ "root": {
+ "a": [
+ {
+ "keep": "me"
+ },
+ "",
+ {
+ "need": "friend",
+ "friend": "me"
+ },
+ {
+ "Camel": "case",
+ "makeme": "lower"
+ }
+ ],
+ "b": {
+ "change": "VAL"
+ }
+ }
+ };
+ const options = {
+ attributeNamePrefix: "",
+ ignoreAttributes: false,
+ parseAttributeValue: true,
+ updateTag(tagName, jPath, attrs){
+ if(attrs["skip"]) delete attrs["skip"]
+ if(attrs["camel"]) {
+ attrs["Camel"] = attrs["camel"];
+ delete attrs["camel"];
+ }
+ if(attrs["need"]) {
+ attrs["friend"] = "me";
+ }
+ if(attrs["MakeMe"]) {
+ attrs["makeme"] = attrs["MakeMe"];
+ delete attrs["MakeMe"];
+ }
+ if(attrs["change"]) {
+ attrs["change"] = attrs["change"].toUpperCase();
+ }
+ return tagName;
+ }
+ };
+
+ const parser = new XMLParser(options);
+ let result = parser.parse(xmlData);
+
+ // console.log(JSON.stringify(result,null,4));
+ expect(result).toEqual(expected);
+
+ result = XMLValidator.validate(xmlData);
+ expect(result).toBe(true);
+ });
+ it("should delete all the attributes", function() {
+ const xmlData = `
+
+
+
+
+
+ `;
+ const expected = {
+ "root": {
+ "a": [
+ "",
+ "",
+ "",
+ ""
+ ],
+ "b": ""
+ }
+ };
+ const options = {
+ attributeNamePrefix: "",
+ ignoreAttributes: false,
+ parseAttributeValue: true,
+ updateTag(tagName, jPath, attrs){
+ for (var k in attrs){
+ if (attrs.hasOwnProperty(k)){
+ delete attrs[k];
+ }
+ }
+ return tagName;
+ }
+ };
+
+ const parser = new XMLParser(options);
+ let result = parser.parse(xmlData);
+
+ // console.log(JSON.stringify(result,null,4));
+ expect(result).toEqual(expected);
+
+ result = XMLValidator.validate(xmlData);
+ expect(result).toBe(true);
+ });
+ it("should skip a tag or modify tag name", function() {
+ const xmlData = `
+
+
+ Post title
+
+
+ some text
+
+ some text 2
+
+
+
+
+ `;
+ const expected = {
+ "html": {
+ "header": "",
+ "body": {
+ "h1": {
+ "#text": "Post title",
+ "class": "highlight underline"
+ },
+ "div": {
+ "p": [
+ "some text",
+ {
+ "#text": "some text 2",
+ "joint": "abcd"
+ }
+ ],
+ "img": {
+ "width": "200",
+ "height": "200"
+ }
+ }
+ }
+ }
+ };
+ const options = {
+ ignoreAttributes: false,
+ attributeNamePrefix: "",
+ updateTag: function(tagname, jPath, attrs){
+
+ if(tagname ==="h1" && attrs["class"] && attrs["class"].indexOf("highlight") > -1){
+ attrs["class"] += " underline"
+ }else if(attrs["join"]){
+ let val = "";
+ Object.keys(attrs).forEach( a => {
+ val+= attrs[a]
+ delete attrs[a];
+ });
+ attrs["joint"] = val;
+ }
+ if(tagname === "script") return false;
+ else if(tagname === "img"){
+ if(attrs.width > 200 || attrs.height > 200) return false;
+ }else if(tagname === "content"){
+ return "div"
+ }
+ return tagname;
+ },
+ unpairedTags: ["img"]
+ };
+ const parser = new XMLParser(options);
+ let result = parser.parse(xmlData);
+
+ // console.log(JSON.stringify(result,null,4));
+ expect(result).toEqual(expected);
+
+ });
+});
diff --git a/src/fxp.d.ts b/src/fxp.d.ts
index 881d146e..d622f27e 100644
--- a/src/fxp.d.ts
+++ b/src/fxp.d.ts
@@ -32,7 +32,14 @@ Control how tag value should be parsed. Called only if tag value is not empty
ignorePiTags: boolean;
transformTagName: ((tagName: string) => string) | false;
transformAttributeName: ((attributeName: string) => string) | false;
- updateAttributes(tagName: string, jPath: string, attrs: {[k: string]: string}): {[k: string]: string};
+ /**
+Change the tag name when a different name is returned. Skip the tag from parsed result when false is returned.
+Modify `attrs` object to control attributes for the given tag.
+
+@returns {string} new tag name.
+@returns false to skip the tag
+ */
+ updateTag: (tagName: string, jPath: string, attrs: {[k: string]: string}) => string | boolean;
};
type strnumOptions = {
hex: boolean;
diff --git a/src/xmlparser/OptionsBuilder.js b/src/xmlparser/OptionsBuilder.js
index 5bf6b29d..85e99252 100644
--- a/src/xmlparser/OptionsBuilder.js
+++ b/src/xmlparser/OptionsBuilder.js
@@ -34,8 +34,8 @@ const defaultOptions = {
ignorePiTags: false,
transformTagName: false,
transformAttributeName: false,
- updateAttributes: function(tagName, attrs, jPath){
- return attrs;
+ updateTag: function(tagName, jPath, attrs){
+ return tagName
}
};
diff --git a/src/xmlparser/OrderedObjParser.js b/src/xmlparser/OrderedObjParser.js
index 6b3ab6d3..4f9bfe8e 100644
--- a/src/xmlparser/OrderedObjParser.js
+++ b/src/xmlparser/OrderedObjParser.js
@@ -50,6 +50,7 @@ class OrderedObjParser{
this.replaceEntitiesValue = replaceEntitiesValue;
this.readStopNodeData = readStopNodeData;
this.saveTextToParentTag = saveTextToParentTag;
+ this.addChild = addChild;
}
}
@@ -171,7 +172,7 @@ function buildAttributesMap(attrStr, jPath, tagName) {
attrCollection[this.options.attributesGroupName] = attrs;
return attrCollection;
}
- return this.options.updateAttributes(tagName, attrs, jPath)
+ return attrs
}
}
@@ -226,7 +227,7 @@ const parseXml = function(xmlData) {
if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
}
- currentNode.addChild(childNode);
+ this.addChild(currentNode, childNode, jPath)
}
@@ -323,7 +324,7 @@ const parseXml = function(xmlData) {
jPath = jPath.substr(0, jPath.lastIndexOf("."));
childNode.add(this.options.textNodeName, tagContent);
- currentNode.addChild(childNode);
+ this.addChild(currentNode, childNode, jPath)
}else{
//selfClosing tag
if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
@@ -343,7 +344,7 @@ const parseXml = function(xmlData) {
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
}
jPath = jPath.substr(0, jPath.lastIndexOf("."));
- currentNode.addChild(childNode);
+ this.addChild(currentNode, childNode, jPath)
}
//opening tag
else{
@@ -353,7 +354,7 @@ const parseXml = function(xmlData) {
if(tagName !== tagExp && attrExpPresent){
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
}
- currentNode.addChild(childNode);
+ this.addChild(currentNode, childNode, jPath)
currentNode = childNode;
}
textData = "";
@@ -367,6 +368,17 @@ const parseXml = function(xmlData) {
return xmlObj.child;
}
+function addChild(currentNode, childNode, jPath){
+ const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"])
+ if(result === false){
+ }else if(typeof result === "string"){
+ childNode.tagname = result
+ currentNode.addChild(childNode);
+ }else{
+ currentNode.addChild(childNode);
+ }
+}
+
const replaceEntitiesValue = function(val){
if(this.options.processEntities){
@@ -423,7 +435,7 @@ function isItStopNode(stopNodes, jPath, currentTagName){
}
/**
- * Returns the tag Expression and where it is ending handling single-dobule quotes situation
+ * Returns the tag Expression and where it is ending handling single-double quotes situation
* @param {string} xmlData
* @param {number} i starting index
* @returns