diff --git a/docs/v4/2.XMLparseOptions.md b/docs/v4/2.XMLparseOptions.md
index 60460dc4..c012222c 100644
--- a/docs/v4/2.XMLparseOptions.md
+++ b/docs/v4/2.XMLparseOptions.md
@@ -131,8 +131,8 @@ const options = {
:
```
-## cdataTagName
-If `cdataTagName` is not set to some property name, then CDATA values are merged to tag's text value.
+## cdataPropName
+If `cdataPropName` is not set to some property name, then CDATA values are merged to tag's text value.
Eg
```js
@@ -152,12 +152,12 @@ Output
}
}
```
-When `cdataTagName` is set to some property then text value and CDATA are parsed to different properties.
+When `cdataPropName` is set to some property then text value and CDATA are parsed to different properties.
Example 2
```js
const options = {
- cdataTagName: "__cdata"
+ cdataPropName: "__cdata"
}
```
Output
@@ -174,7 +174,44 @@ Output
}
}
```
+## commentPropName
+If `commentPropName` is set to some property name, then comments are parsed from XML.
+Eg
+```js
+const xmlData = `
+
+
+
+
+
+ Tanmay
+ A
+
+ `;
+
+const options = {
+ commentPropName: "#comment"
+};
+const parser = new XMLParser(options);
+const output = parser.parse(xmlDataStr);
+```
+Output
+```json
+{
+ "#comment": "Students grades are uploaded by months",
+ "class_list": {
+ "student": {
+ "#comment": [
+ "Student details",
+ "A second comment"
+ ],
+ "name": "Tanmay",
+ "grade": "A"
+ }
+ }
+}
+```
## ignoreAttributes
@@ -403,8 +440,65 @@ Output
}
}
```
+## preserveOrder
+When `preserveOrder: true` , you get some long ugly result. That's because this option is used to keep the order of tags in result js Object. It also helps XML builder to build the similar kind of XML from the js object without losing information.
+```js
+const XMLdata = `
+
+
+
+
+
+ Tanmay
+ A
+
+ `;
+ const options = {
+ commentPropName: "#comment",
+ preserveOrder: true
+ };
+ const parser = new XMLParser(options);
+ let result = parser.parse(XMLdata);
+```
+```json
+[
+ {
+ "#comment": [
+ { "#text": "Students grades are uploaded by months" }
+ ]
+ },
+ {
+ "class_list": [
+ {
+ "student": [
+ {
+ "#comment": [
+ { "#text": "Student details" }
+ ]
+ },
+ {
+ "#comment": [
+ { "#text": "A second comment" }
+ ]
+ },
+ {
+ "name": [
+ { "#text": "Tanmay" }
+ ]
+ },
+ {
+ "grade": [
+ { "#text": "A" }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+]
+```
## removeNSPrefix
Remove namespace string from tag and attribute names.
diff --git a/docs/v4/3.XMLBuilder.md b/docs/v4/3.XMLBuilder.md
index 053f7fb4..1cc88011 100644
--- a/docs/v4/3.XMLBuilder.md
+++ b/docs/v4/3.XMLBuilder.md
@@ -59,8 +59,10 @@ To recognize attribute properties group in the JS object so that they can be tri
## attributeValueProcessor
To customize the bahaviour of parsing an attribute value to the string value. It accepts attribute name and value.
-## cdataTagName
-To recognize CDATA properties in a JS object so that they can be transformed correcty. This option is useful only if `preserveOrder: true`
+## cdataPropName
+To recognize CDATA properties in a JS object so that they can be transformed correcty. This option is supported only if `preserveOrder: true`
+## commentPropName
+To recognize commentsin a JS object so that they can be transformed correcty. This option is supported only if `preserveOrder: true` because position of comment is important.
## format
By default, parsed XML is single line XML string. By `format: true`, you can format it for better view.
diff --git a/spec/attr_spec.js b/spec/attr_spec.js
index 60ab5e6a..3c8d2ae5 100644
--- a/spec/attr_spec.js
+++ b/spec/attr_spec.js
@@ -46,7 +46,7 @@ describe("XMLParser", function() {
const parser = new XMLParser(options);
let result = parser.parse(xmlData);
- console.log(JSON.stringify(result,null,4));
+ // console.log(JSON.stringify(result,null,4));
// expect(result).toEqual(expected);
result = XMLValidator.validate(xmlData);
diff --git a/spec/cdata_spec.js b/spec/cdata_spec.js
index 3baba7a5..bb4f17d2 100644
--- a/spec/cdata_spec.js
+++ b/spec/cdata_spec.js
@@ -261,7 +261,7 @@ patronymic`;
const options = {
ignoreAttributes: false,
- cdataTagName: "__cdata"
+ cdataPropName: "__cdata"
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData);
@@ -297,7 +297,7 @@ patronymic`;
const options = {
ignoreAttributes: false,
- cdataTagName: "__cdata"
+ cdataPropName: "__cdata"
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData);
diff --git a/spec/comments_spec.js b/spec/comments_spec.js
new file mode 100644
index 00000000..24c3e832
--- /dev/null
+++ b/spec/comments_spec.js
@@ -0,0 +1,36 @@
+
+const { XMLParser, XMLBuilder, XMLValidator} = require("../src/fxp");
+
+describe("Comments", function() {
+
+ it("should parse comment and build them back", function() {
+ const XMLdata = `
+
+
+
+
+
+ Tanmay
+ A
+
+ `;
+
+ const options = {
+ ignoreAttributes: false,
+ format: true,
+ commentPropName: "#comment",
+ preserveOrder: true
+ };
+ const parser = new XMLParser(options);
+ let result = parser.parse(XMLdata);
+ // console.log(JSON.stringify(result, null,4));
+
+ const builder = new XMLBuilder(options);
+ const output = builder.build(result);
+ // console.log(output);
+ expect(output.replace(/\s+/g, "")).toEqual(XMLdata.replace(/\s+/g, ""));
+});
+
+
+});
+
diff --git a/spec/inputType_spec.js b/spec/inputType_spec.js
index 7affeb75..32284a18 100644
--- a/spec/inputType_spec.js
+++ b/spec/inputType_spec.js
@@ -46,21 +46,41 @@ describe("XMLParser", function() {
expect(result).toEqual(expected);
});
- it("should not parse when invalid value is given", function() {
+ xit("should not parse when invalid value is given", function() {
+ const parser = new XMLParser();
+ const result = parser.parse(23);
+ // console.log(result)
+ expect(result).toBeUndefined();
+ });
+ xit("should not parse when invalid value is given", function() {
const parser = new XMLParser();
- expect(parser.parse(23)).toBeUndefined();
+ const result = parser.parse([]);
+ // console.log(result)
+ expect(result).toBeUndefined();
});
- it("should not parse when invalid value is given", function() {
+ xit("should not parse when invalid value is given", function() {
+ const parser = new XMLParser( { preserveOrder: true});
+ const result = parser.parse([]);
+ expect(result).toBeUndefined();
+ });
+ it("should not parse when null", function() {
const parser = new XMLParser( { preserveOrder: true});
- expect(parser.parse([])).toBeUndefined();
+ expect(() => {
+ parser.parse(null);
+ // console.log(result);
+ }).toThrowError("Cannot read property 'toString' of null");
});
-
- it("should not parse when invalid value is given", function() {
- const parser = new XMLParser();
- expect(parser.parse([])).toBeUndefined();
+ it("should not parse when undefined", function() {
+ const parser = new XMLParser( { preserveOrder: true});
+ expect(() => {
+ parser.parse();
+ // console.log(result);
+ }).toThrowError("Cannot read property 'toString' of undefined");
});
+
+
});
diff --git a/spec/j2x_ordered_spec.js b/spec/j2x_ordered_spec.js
index 5d2c60e8..e8e21f53 100644
--- a/spec/j2x_ordered_spec.js
+++ b/spec/j2x_ordered_spec.js
@@ -62,7 +62,7 @@ describe("XMLBuilder", function() {
const options = {
ignoreAttributes: false,
preserveOrder: true,
- cdataTagName: "#CDATA",
+ cdataPropName: "#CDATA",
allowBooleanAttributes: true,
// format: true,
@@ -173,7 +173,7 @@ describe("XMLBuilder", function() {
const options = {
preserveOrder: true,
format: true,
- // cdataTagName: "#CDATA"
+ // cdataPropName: "#CDATA"
}
const parser = new XMLParser(options);
let result = parser.parse(XMLdata);
@@ -208,7 +208,7 @@ describe("XMLBuilder", function() {
const options = {
preserveOrder: true,
format: true,
- cdataTagName: "#CDATA"
+ cdataPropName: "#CDATA"
}
const parser = new XMLParser(options);
let result = parser.parse(XMLdata);
diff --git a/spec/j2x_spec.js b/spec/j2x_spec.js
index e6f11bb4..1c79a95e 100644
--- a/spec/j2x_spec.js
+++ b/spec/j2x_spec.js
@@ -222,7 +222,7 @@ describe("XMLBuilder", function() {
}
};
const builder = new XMLBuilder({
- cdataTagName: "__cdata",
+ cdataPropName: "__cdata",
attributesGroupName: "@",
encodeHTMLchar: true,
format: true,
@@ -274,7 +274,7 @@ textvalue>
}
};
const builder = new XMLBuilder({
- cdataTagName: "__cdata",
+ cdataPropName: "__cdata",
attributesGroupName: "@",
encodeHTMLchar: true,
format: true,
@@ -358,7 +358,7 @@ textvalue>
attributesGroupName: "$",
textNodeName: "_",
ignoreAttributes: false,
- cdataTagName: "$cdata",
+ cdataPropName: "$cdata",
cdataPositionChar: "\\c",
format: false,
indentBy: "\t",
diff --git a/spec/valueProcessors_spec.js b/spec/valueProcessors_spec.js
index 8bd6ccc3..140938cd 100644
--- a/spec/valueProcessors_spec.js
+++ b/spec/valueProcessors_spec.js
@@ -273,7 +273,7 @@ describe("XMLParser", function() {
const options = {
ignoreAttributes: false,
preserveOrder: true,
- cdataTagName: "#CDATA",
+ cdataPropName: "#CDATA",
tagValueProcessor: (tagName, tagValue, jPath, hasAttributes, isLeafNode) => {
// console.log(tagName, tagValue, jPath, hasAttributes, isLeafNode);
tagValueProcessorCalls.push(`${tagName} ${tagValue} ${jPath} ${hasAttributes} ${isLeafNode}`);
@@ -301,7 +301,7 @@ describe("XMLParser", function() {
const options = {
ignoreAttributes: false,
preserveOrder: true,
- cdataTagName: "#CDATA",
+ cdataPropName: "#CDATA",
tagValueProcessor: (tagName, tagValue, jPath, hasAttributes, isLeafNode) => {
// console.log(tagName, tagValue, jPath, hasAttributes, isLeafNode);
tagValueProcessorCalls.push(`${tagName} ${tagValue} ${jPath} ${hasAttributes} ${isLeafNode}`);
diff --git a/src/fxp.d.ts b/src/fxp.d.ts
index b46b4954..7e901589 100644
--- a/src/fxp.d.ts
+++ b/src/fxp.d.ts
@@ -9,7 +9,8 @@ type X2jOptions = {
parseTagValue: boolean;
parseAttributeValue: boolean;
trimValues: boolean;
- cdataTagName: false | string;
+ cdataPropName: false | string;
+ commentPropName: false | string;
tagValueProcessor: (tagName: string, tagValue: string, jPath: string, hasAttributes: boolean, isLeafNode: boolean) => string;
attributeValueProcessor: (attrName: string, attrValue: string, jPath: string) => string;
numberParseOptions: strnumOptions;
@@ -33,7 +34,8 @@ type XmlBuilderOptions = {
attributesGroupName: false | string;
textNodeName: string;
ignoreAttributes: boolean;
- cdataTagName: false | string;
+ cdataPropName: false | string;
+ commentPropName: false | string;
format: boolean;
indentBy: string;
arrayNodeName: string;
diff --git a/src/xmlbuilder/json2xml.js b/src/xmlbuilder/json2xml.js
index cb4162cf..6e5e6646 100644
--- a/src/xmlbuilder/json2xml.js
+++ b/src/xmlbuilder/json2xml.js
@@ -8,7 +8,7 @@ const defaultOptions = {
attributesGroupName: false,
textNodeName: '#text',
ignoreAttributes: true,
- cdataTagName: false,
+ cdataPropName: false,
format: false,
indentBy: ' ',
suppressEmptyNode: false,
@@ -18,7 +18,8 @@ const defaultOptions = {
attributeValueProcessor: function(attrName, a) {
return a;
},
- preserveOrder: false
+ preserveOrder: false,
+ commentPropName: false
};
const props = [
@@ -26,7 +27,7 @@ const props = [
'attributesGroupName',
'textNodeName',
'ignoreAttributes',
- 'cdataTagName',
+ 'cdataPropName',
'format',
'indentBy',
'suppressEmptyNode',
@@ -34,6 +35,7 @@ const props = [
'attributeValueProcessor',
'arrayNodeName', //when array as root
'preserveOrder',
+ "commentPropName",
// 'rootNodeName', //when jsObject have multiple properties on root level
];
diff --git a/src/xmlbuilder/orderedJs2Xml.js b/src/xmlbuilder/orderedJs2Xml.js
index 91a77b21..5c585520 100644
--- a/src/xmlbuilder/orderedJs2Xml.js
+++ b/src/xmlbuilder/orderedJs2Xml.js
@@ -1,7 +1,13 @@
const {EOL} = require('os');
-function toXml(jObj, options){
- return arrToStr( [jObj], options, 0);
+/**
+ *
+ * @param {array} jArray
+ * @param {any} options
+ * @returns
+ */
+function toXml(jArray, options){
+ return arrToStr( jArray, options, 0);
}
function arrToStr(arr, options, level){
@@ -19,9 +25,12 @@ function arrToStr(arr, options, level){
if(tagName === options.textNodeName){
xmlStr += indentation + options.tagValueProcessor( tagName, tagObj[tagName]);
continue;
- }else if( tagName === options.cdataTagName){
+ }else if( tagName === options.cdataPropName){
xmlStr += indentation + ``;
continue;
+ }else if( tagName === options.commentPropName){
+ xmlStr += indentation + ``;
+ continue;
}
const attStr = attr_to_str(tagObj.attributes, options);
let tagStart = indentation + `<${tagName}${attStr}`;
diff --git a/src/xmlparser/OptionsBuilder.js b/src/xmlparser/OptionsBuilder.js
index 6d83ad2c..5048cc74 100644
--- a/src/xmlparser/OptionsBuilder.js
+++ b/src/xmlparser/OptionsBuilder.js
@@ -11,7 +11,7 @@ const defaultOptions = {
parseTagValue: true,
parseAttributeValue: false,
trimValues: true, //Trim string values of tag and attributes
- cdataTagName: false,
+ cdataPropName: false,
numberParseOptions: {
hex: true,
leadingZeros: true
@@ -24,7 +24,8 @@ const defaultOptions = {
},
stopNodes: [], //nested tags will not be parsed even for errors
alwaysCreateTextNode: false,
- isArray: () => false
+ isArray: () => false,
+ commentPropName: false,
};
const props = [
@@ -38,13 +39,14 @@ const props = [
'parseTagValue',
'parseAttributeValue',
'trimValues',
- 'cdataTagName',
+ 'cdataPropName',
'tagValueProcessor',
'attributeValueProcessor',
'numberParseOptions',
'stopNodes',
'alwaysCreateTextNode',
'isArray',
+ 'commentPropName'
];
const util = require('../util');
diff --git a/src/xmlparser/OrderedObjParser.js b/src/xmlparser/OrderedObjParser.js
index b965fc4b..6bfd3a70 100644
--- a/src/xmlparser/OrderedObjParser.js
+++ b/src/xmlparser/OrderedObjParser.js
@@ -190,7 +190,26 @@ const parseToOrderedJsObj = function(xmlData, options) {
} else if( xmlData[i+1] === '?') {
i = findClosingIndex(xmlData, "?>", i, "Pi Tag is not closed.")
} else if(xmlData.substr(i + 1, 3) === '!--') {
- i = findClosingIndex(xmlData, "-->", i, "Comment is not closed.")
+ const endIndex = findClosingIndex(xmlData, "-->", i, "Comment is not closed.")
+ if(options.commentPropName){
+ const comment = xmlData.substring(i + 4, endIndex - 2);
+
+ //TODO: remove repeated code
+ if(textData){ //store previously collected data as textNode
+ textData = parseValue(textData
+ , options
+ , currentNode.tagname
+ , jPath
+ ,false
+ , currentNode.attrsMap ? Object.keys(currentNode.attrsMap).length !== 0 : false
+ , Object.keys(currentNode.child).length === 0);
+
+ if(textData !== undefined && textData !== "") currentNode.add(options.textNodeName, textData);
+ textData = "";
+ }
+ currentNode.add(options.commentPropName, [ { [options.textNodeName] : comment } ]);
+ }
+ i = endIndex;
} else if( xmlData.substr(i + 1, 2) === '!D') {
const closeIndex = findClosingIndex(xmlData, ">", i, "DOCTYPE is not closed.")
const tagExp = xmlData.substring(i, closeIndex);
@@ -217,10 +236,10 @@ const parseToOrderedJsObj = function(xmlData, options) {
}
//cdata should be set even if it is 0 length string
- if(options.cdataTagName){
- let val = parseValue(tagExp, options, options.cdataTagName, jPath + "." + options.cdataTagName, true, false, true);
+ if(options.cdataPropName){
+ let val = parseValue(tagExp, options, options.cdataPropName, jPath + "." + options.cdataPropName, true, false, true);
if(!val) val = "";
- currentNode.add(options.cdataTagName, [ { [options.textNodeName] : val } ]);
+ currentNode.add(options.cdataPropName, [ { [options.textNodeName] : val } ]);
}else{
let val = parseValue(tagExp, options, currentNode.tagname, jPath, true, false, true);
if(!val) val = "";
@@ -304,7 +323,7 @@ const parseToOrderedJsObj = function(xmlData, options) {
textData += xmlData[i];
}
}
- return xmlObj.child[0];
+ return xmlObj.child;
}
//TODO: use jPath to simplify the logic
diff --git a/src/xmlparser/node2json.js b/src/xmlparser/node2json.js
index 5d08785c..3a952832 100644
--- a/src/xmlparser/node2json.js
+++ b/src/xmlparser/node2json.js
@@ -1,7 +1,13 @@
'use strict';
+/**
+ *
+ * @param {array} node
+ * @param {any} options
+ * @returns
+ */
function prettify(node, options){
- return compress( [node], options);
+ return compress( node, options);
}
/**