fast-xml-parser 4.0.0-beta.7 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,26 +1,43 @@
1
1
  Note: If you find missing information about particular minor version, that version must have been changed without any functional change in this library.
2
2
 
3
- ** 4.0.0-beta.7 / 2021-12-09**
3
+ **4.0.2 / 2022-02-04**
4
+ * builder supports `suppressUnpairedNode`
5
+ * parser supports `ignoreDeclaration` and `ignorePiTags`
6
+ * fix: when comment is parsed as text value if given as `<!--> ...` #423
7
+ * builder supports decoding `&`
8
+
9
+ **4.0.1 / 2022-01-08**
10
+ * fix builder for pi tag
11
+ * fix: support suppressBooleanAttrs by builder
12
+
13
+ **4.0.0 / 2022-01-06**
14
+ * Generating different combined, parser only, builder only, validator only browser bundles
15
+ * Keeping cjs modules as they can be imported in cjs and esm modules both. Otherwise refer `esm` branch.
16
+
17
+ **4.0.0-beta.8 / 2021-12-13**
18
+ * call tagValueProcessor for stop nodes
19
+
20
+ **4.0.0-beta.7 / 2021-12-09**
4
21
  * fix Validator bug when an attribute has no value but '=' only
5
22
  * XML Builder should suppress unpaired tags by default.
6
23
  * documents update for missing features
7
24
  * refactoring to use Object.assign
8
25
  * refactoring to remove repeated code
9
26
 
10
- ** 4.0.0-beta.6 / 2021-12-05**
27
+ **4.0.0-beta.6 / 2021-12-05**
11
28
  * Support PI Tags processing
12
29
  * Support `suppressBooleanAttributes` by XML Builder for attributes with value `true`.
13
30
 
14
- ** 4.0.0-beta.5 / 2021-12-04**
31
+ **4.0.0-beta.5 / 2021-12-04**
15
32
  * fix: when a tag with name "attributes"
16
33
 
17
- ** 4.0.0-beta.4 / 2021-12-02**
34
+ **4.0.0-beta.4 / 2021-12-02**
18
35
  * Support HTML document parsing
19
36
  * skip stop nodes parsing when building the XML from JS object
20
37
  * Support external entites without DOCTYPE
21
38
  * update dev dependency: strnum v1.0.5 to fix long number issue
22
39
 
23
- ** 4.0.0-beta.3 / 2021-11-30**
40
+ **4.0.0-beta.3 / 2021-11-30**
24
41
  * support global stopNodes expression like "*.stop"
25
42
  * support self-closing and paired unpaired tags
26
43
  * fix: CDATA should not be parsed.
package/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # [fast-xml-parser](https://www.npmjs.com/package/fast-xml-parser)
2
2
  [![Backers on Open Collective](https://opencollective.com/fast-xml-parser/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/fast-xml-parser/sponsors/badge.svg)](#sponsors) [![Known Vulnerabilities](https://snyk.io/test/github/naturalintelligence/fast-xml-parser/badge.svg)](https://snyk.io/test/github/naturalintelligence/fast-xml-parser)
3
3
  [![NPM quality][quality-image]][quality-url]
4
- [![Travis ci Build Status](https://travis-ci.org/NaturalIntelligence/fast-xml-parser.svg?branch=master)](https://travis-ci.org/NaturalIntelligence/fast-xml-parser)
5
4
  [![Coverage Status](https://coveralls.io/repos/github/NaturalIntelligence/fast-xml-parser/badge.svg?branch=master)](https://coveralls.io/github/NaturalIntelligence/fast-xml-parser?branch=master)
6
5
  [<img src="https://img.shields.io/badge/Try-me-blue.svg?colorA=FFA500&colorB=0000FF" alt="Try me"/>](https://naturalintelligence.github.io/fast-xml-parser/)
7
6
  [![NPM total downloads](https://img.shields.io/npm/dt/fast-xml-parser.svg)](https://npm.im/fast-xml-parser)
@@ -90,7 +89,7 @@ const xmlContent = builder.build(jObj);
90
89
 
91
90
  In a HTML page
92
91
  ```html
93
- <script src="path/to/fxparser.js"></script>
92
+ <script src="path/to/fxp.min.js"></script>
94
93
  :
95
94
  <script>
96
95
  const parser = new fxparser.XMLParser();
@@ -98,6 +97,16 @@ In a HTML page
98
97
  </script>
99
98
  ```
100
99
 
100
+ Check lib folder for different browser bundles
101
+
102
+ | Bundle Name | Size |
103
+ | -- | -- |
104
+ | fxbuilder.min.js | 5.2K |
105
+ | fxparser.js | 50K |
106
+ | fxparser.min.js | 17K |
107
+ | fxp.min.js | 22K |
108
+ | fxvalidator.min.js | 5.7K |
109
+
101
110
  ### Documents
102
111
  **v3**
103
112
  * [documents](./docs/v3/docs.md)
@@ -107,9 +116,9 @@ In a HTML page
107
116
  2. [XML Parser](./docs/v4/2.XMLparseOptions.md)
108
117
  3. [XML Builder](./docs/v4/3.XMLBuilder.md)
109
118
  4. [XML Validator](./docs/v4/4.XMLValidator.md)
110
- 5. [Entites](./docs/5.Entities.md)
111
- 6. [HTML Document Parsing](./docs/6.HTMLParsing.md)
112
- 7. [PI Tag processing](./docs/7.PITags.md)
119
+ 5. [Entites](./docs/v4/5.Entities.md)
120
+ 6. [HTML Document Parsing](./docs/v4/6.HTMLParsing.md)
121
+ 7. [PI Tag processing](./docs/v4/7.PITags.md)
113
122
  ## Performance
114
123
 
115
124
  ### XML Parser
@@ -135,6 +144,7 @@ In a HTML page
135
144
  * Run tests for a route or from a route
136
145
  * Customizable reporting
137
146
  * Central dashboard for better monitoring
147
+ * Options to integrate E2E tests with Jira, Github etc using Central dashboard `Tian`.
138
148
  * **[Stubmatic](https://github.com/NaturalIntelligence/Stubmatic)** : Create fake webservices, DynamoDB or S3 servers, Manage fake/mock stub data, Or fake any HTTP(s) call.
139
149
 
140
150
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-xml-parser",
3
- "version": "4.0.0-beta.7",
3
+ "version": "4.0.2",
4
4
  "description": "Validate XML, Parse XML, Build XML without C/C++ based libraries",
5
5
  "main": "./src/fxp.js",
6
6
  "scripts": {
@@ -9,7 +9,7 @@
9
9
  "coverage": "nyc report --reporter html --reporter text -t .nyc_output --report-dir .nyc_output/summary",
10
10
  "perf": "node ./benchmark/perfTest3.js",
11
11
  "lint": "eslint src/*.js spec/*.js",
12
- "bundle": "webpack && webpack --config webpack-prod.config.js",
12
+ "bundle": "webpack --config webpack-prod.config.js",
13
13
  "prettier": "prettier --write src/**/*.js",
14
14
  "publish-please": "publish-please",
15
15
  "checkReadiness": "publish-please --dry-run"
@@ -47,6 +47,7 @@
47
47
  "@babel/preset-env": "^7.13.10",
48
48
  "@babel/register": "^7.13.8",
49
49
  "babel-loader": "^8.2.2",
50
+ "cytorus": "^0.2.9",
50
51
  "eslint": "^8.3.0",
51
52
  "he": "^1.2.0",
52
53
  "jasmine": "^3.6.4",
package/src/fxp.d.ts CHANGED
@@ -20,6 +20,8 @@ type X2jOptions = {
20
20
  isArray: (tagName: string, jPath: string, isLeafNode: boolean, isAttribute: boolean) => boolean;
21
21
  processEntities: boolean;
22
22
  htmlEntities: boolean;
23
+ ignoreDeclaration: boolean;
24
+ ignorePiTags: boolean;
23
25
  };
24
26
  type strnumOptions = {
25
27
  hex: boolean;
@@ -44,6 +46,7 @@ type XmlBuilderOptions = {
44
46
  indentBy: string;
45
47
  arrayNodeName: string;
46
48
  suppressEmptyNode: boolean;
49
+ suppressUnpairedNode: boolean;
47
50
  suppressBooleanAttributes: boolean;
48
51
  preserveOrder: boolean;
49
52
  unpairedTags: string[];
package/src/fxp_cjs ADDED
@@ -0,0 +1,8 @@
1
+
2
+
3
+ const XMLValidator = require('./validator.js');
4
+ const XMLParser = require('./xmlparser/XMLParser.js');
5
+ const XMLBuilder = require('./xmlbuilder/json2xml.js');
6
+
7
+ module.exports = {XMLParser,XMLValidator, XMLBuilder};
8
+
@@ -11,6 +11,7 @@ const defaultOptions = {
11
11
  format: false,
12
12
  indentBy: ' ',
13
13
  suppressEmptyNode: false,
14
+ suppressUnpairedNode: true,
14
15
  suppressBooleanAttributes: true,
15
16
  tagValueProcessor: function(key, a) {
16
17
  return a;
@@ -21,12 +22,13 @@ const defaultOptions = {
21
22
  preserveOrder: false,
22
23
  commentPropName: false,
23
24
  unpairedTags: [],
24
- entities: {
25
- ">" : { regex: new RegExp(">", "g"), val: "&gt;" },
26
- "<" : { regex: new RegExp("<", "g"), val: "&lt;" },
27
- "sQuot" : { regex: new RegExp("\'", "g"), val: "&apos;" },
28
- "dQuot" : { regex: new RegExp("\"", "g"), val: "&quot;" }
29
- },
25
+ entities: [
26
+ { regex: new RegExp("&", "g"), val: "&amp;" },//it must be on top
27
+ { regex: new RegExp(">", "g"), val: "&gt;" },
28
+ { regex: new RegExp("<", "g"), val: "&lt;" },
29
+ { regex: new RegExp("\'", "g"), val: "&apos;" },
30
+ { regex: new RegExp("\"", "g"), val: "&quot;" }
31
+ ],
30
32
  processEntities: true,
31
33
  stopNodes: []
32
34
  };
@@ -68,6 +70,7 @@ function Builder(options) {
68
70
  this.buildObjectNode = buildObjectNode;
69
71
 
70
72
  this.replaceEntitiesValue = replaceEntitiesValue;
73
+ this.buildAttrPairStr = buildAttrPairStr;
71
74
  }
72
75
 
73
76
  Builder.prototype.build = function(jObj) {
@@ -90,16 +93,16 @@ Builder.prototype.j2x = function(jObj, level) {
90
93
  if (typeof jObj[key] === 'undefined') {
91
94
  // supress undefined node
92
95
  } else if (jObj[key] === null) {
93
- val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
96
+ if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
97
+ else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
98
+ // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
94
99
  } else if (jObj[key] instanceof Date) {
95
100
  val += this.buildTextNode(jObj[key], key, '', level);
96
101
  } else if (typeof jObj[key] !== 'object') {
97
102
  //premitive type
98
103
  const attr = this.isAttribute(key);
99
104
  if (attr) {
100
- let val = this.options.attributeValueProcessor(attr, '' + jObj[key]);
101
- val = this.replaceEntitiesValue(val);
102
- attrStr += ' ' + attr + '="' + val + '"';
105
+ attrStr += this.buildAttrPairStr(attr, '' + jObj[key]);
103
106
  }else {
104
107
  //tag value
105
108
  if (key === this.options.textNodeName) {
@@ -117,7 +120,9 @@ Builder.prototype.j2x = function(jObj, level) {
117
120
  if (typeof item === 'undefined') {
118
121
  // supress undefined node
119
122
  } else if (item === null) {
120
- val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
123
+ if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
124
+ else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
125
+ // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
121
126
  } else if (typeof item === 'object') {
122
127
  val += this.processTextOrObjNode(item, key, level)
123
128
  } else {
@@ -130,9 +135,7 @@ Builder.prototype.j2x = function(jObj, level) {
130
135
  const Ks = Object.keys(jObj[key]);
131
136
  const L = Ks.length;
132
137
  for (let j = 0; j < L; j++) {
133
- let val = this.options.attributeValueProcessor(Ks[j], '' + jObj[key][Ks[j]]);
134
- val = this.replaceEntitiesValue(val);
135
- attrStr += ' ' + Ks[j] + '="' + val + '"';
138
+ attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]);
136
139
  }
137
140
  } else {
138
141
  val += this.processTextOrObjNode(jObj[key], key, level)
@@ -142,6 +145,14 @@ Builder.prototype.j2x = function(jObj, level) {
142
145
  return {attrStr: attrStr, val: val};
143
146
  };
144
147
 
148
+ function buildAttrPairStr(attrName, val){
149
+ val = this.options.attributeValueProcessor(attrName, '' + val);
150
+ val = this.replaceEntitiesValue(val);
151
+ if (this.options.suppressBooleanAttributes && val === "true") {
152
+ return ' ' + attrName;
153
+ } else return ' ' + attrName + '="' + val + '"';
154
+ }
155
+
145
156
  function processTextOrObjNode (object, key, level) {
146
157
  const result = this.j2x(object, level + 1);
147
158
  if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
@@ -152,34 +163,24 @@ function processTextOrObjNode (object, key, level) {
152
163
  }
153
164
 
154
165
  function buildObjectNode(val, key, attrStr, level) {
166
+ let tagEndExp = '</' + key + this.tagEndChar;
167
+ let piClosingChar = "";
168
+
169
+ if(key[0] === "?") {
170
+ piClosingChar = "?";
171
+ tagEndExp = "";
172
+ }
173
+
155
174
  if (attrStr && val.indexOf('<') === -1) {
156
175
  return (
157
- this.indentate(level) +
158
- '<' +
159
- key +
160
- attrStr +
161
- '>' +
176
+ this.indentate(level) + '<' + key + attrStr + piClosingChar + '>' +
162
177
  val +
163
- //+ this.newLine
164
- // + this.indentate(level)
165
- '</' +
166
- key +
167
- this.tagEndChar
168
- );
178
+ tagEndExp );
169
179
  } else {
170
180
  return (
171
- this.indentate(level) +
172
- '<' +
173
- key +
174
- attrStr +
175
- this.tagEndChar +
181
+ this.indentate(level) + '<' + key + attrStr + piClosingChar + this.tagEndChar +
176
182
  val +
177
- //+ this.newLine
178
- this.indentate(level) +
179
- '</' +
180
- key +
181
- this.tagEndChar
182
- );
183
+ this.indentate(level) + tagEndExp );
183
184
  }
184
185
  }
185
186
 
@@ -187,32 +188,32 @@ function buildEmptyObjNode(val, key, attrStr, level) {
187
188
  if (val !== '') {
188
189
  return this.buildObjectNode(val, key, attrStr, level);
189
190
  } else {
190
- return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
191
- //+ this.newLine
191
+ if(key[0] === "?") return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
192
+ else return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
192
193
  }
193
194
  }
194
195
 
195
196
  function buildTextValNode(val, key, attrStr, level) {
196
- let textValue = this.options.tagValueProcessor(key, val);
197
- textValue = this.replaceEntitiesValue(textValue);
198
-
199
- return (
200
- this.indentate(level) +
201
- '<' +
202
- key +
203
- attrStr +
204
- '>' +
205
- textValue +
206
- '</' +
207
- key +
208
- this.tagEndChar
209
- );
197
+ const textValue = this.replaceEntitiesValue(val);
198
+
199
+ if( textValue === '' && this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
200
+ if(this.options.suppressUnpairedNode){
201
+ return this.indentate(level) + '<' + key + this.tagEndChar;
202
+ }else{
203
+ return this.indentate(level) + '<' + key + "/" + this.tagEndChar;
204
+ }
205
+ }else{
206
+ return (
207
+ this.indentate(level) + '<' + key + attrStr + '>' +
208
+ textValue +
209
+ '</' + key + this.tagEndChar );
210
+ }
210
211
  }
211
212
 
212
213
  function replaceEntitiesValue(textValue){
213
214
  if(textValue && textValue.length > 0 && this.options.processEntities){
214
- for (const entityName in this.options.entities) {
215
- const entity = this.options.entities[entityName];
215
+ for (let i=0; i<this.options.entities.length; i++) {
216
+ const entity = this.options.entities[i];
216
217
  textValue = textValue.replace(entity.regex, entity.val);
217
218
  }
218
219
  }
@@ -220,12 +221,17 @@ function replaceEntitiesValue(textValue){
220
221
  }
221
222
 
222
223
  function buildEmptyTextNode(val, key, attrStr, level) {
223
- if( val === '' && this.options.unpairedTags.indexOf(key) !== -1){
224
- return this.indentate(level) + '<' + key + attrStr + this.tagEndChar;
225
- }else if (val !== '') {
224
+ if( val === '' && this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
225
+ if(this.options.suppressUnpairedNode){
226
+ return this.indentate(level) + '<' + key + this.tagEndChar;
227
+ }else{
228
+ return this.indentate(level) + '<' + key + "/" + this.tagEndChar;
229
+ }
230
+ }else if (val !== '') { //empty
226
231
  return this.buildTextValNode(val, key, attrStr, level);
227
232
  } else {
228
- return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
233
+ if(key[0] === "?") return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar; //PI tag
234
+ else return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar; //normal
229
235
  }
230
236
  }
231
237
 
@@ -1,4 +1,4 @@
1
- const {EOL} = require('os');
1
+ const EOL = "\n";
2
2
 
3
3
  /**
4
4
  *
@@ -48,7 +48,8 @@ function arrToStr(arr, options, jPath, level){
48
48
  let tagStart = indentation + `<${tagName}${attStr}`;
49
49
  let tagValue = arrToStr(tagObj[tagName], options, newJPath, level + 1);
50
50
  if(options.unpairedTags.indexOf(tagName) !== -1){
51
- xmlStr += tagStart + ">";
51
+ if(options.suppressUnpairedNode) xmlStr += tagStart + ">";
52
+ else xmlStr += tagStart + "/>";
52
53
  }else if( (!tagValue || tagValue.length === 0) && options.suppressEmptyNode){
53
54
  xmlStr += tagStart + "/>";
54
55
  }else{
@@ -95,8 +96,8 @@ function isStopNode(jPath, options){
95
96
 
96
97
  function replaceEntitiesValue(textValue, options){
97
98
  if(textValue && textValue.length > 0 && options.processEntities){
98
- for (const entityName in options.entities) {
99
- const entity = options.entities[entityName];
99
+ for (let i=0; i< options.entities.length; i++) {
100
+ const entity = options.entities[i];
100
101
  textValue = textValue.replace(entity.regex, entity.val);
101
102
  }
102
103
  }
@@ -29,6 +29,8 @@ const defaultOptions = {
29
29
  unpairedTags: [],
30
30
  processEntities: true,
31
31
  htmlEntities: false,
32
+ ignoreDeclaration: false,
33
+ ignorePiTags: false
32
34
  };
33
35
 
34
36
  const buildOptions = function(options) {
@@ -72,14 +72,15 @@ function addExternalEntities(externalEntities){
72
72
  * @param {boolean} dontTrim
73
73
  * @param {boolean} hasAttributes
74
74
  * @param {boolean} isLeafNode
75
+ * @param {boolean} escapeEntities
75
76
  */
76
- function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode) {
77
+ function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
77
78
  if (val !== undefined) {
78
79
  if (this.options.trimValues && !dontTrim) {
79
80
  val = val.trim();
80
81
  }
81
82
  if(val.length > 0){
82
- val = this.replaceEntitiesValue(val);
83
+ if(!escapeEntities) val = this.replaceEntitiesValue(val);
83
84
 
84
85
  const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode);
85
86
  if(newval === null || newval === undefined){
@@ -193,14 +194,7 @@ const parseXml = function(xmlData) {
193
194
  }
194
195
 
195
196
  if(currentNode){
196
- textData = this.parseTextData(textData
197
- , currentNode.tagname
198
- , jPath
199
- ,false
200
- , currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false
201
- , Object.keys(currentNode.child).length === 0);
202
- if(textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData);
203
- textData = "";
197
+ textData = this.saveTextToParentTag(textData, currentNode, jPath);
204
198
  }
205
199
 
206
200
  jPath = jPath.substr(0, jPath.lastIndexOf("."));
@@ -209,21 +203,29 @@ const parseXml = function(xmlData) {
209
203
  textData = "";
210
204
  i = closeIndex;
211
205
  } else if( xmlData[i+1] === '?') {
206
+
212
207
  let tagData = readTagExp(xmlData,i, false, "?>");
213
208
  if(!tagData) throw new Error("Pi Tag is not closed.");
209
+
214
210
  textData = this.saveTextToParentTag(textData, currentNode, jPath);
211
+ if( (this.options.ignoreDeclaration && tagData.tagName === "?xml") || this.options.ignorePiTags){
212
+
213
+ }else{
214
+
215
+ const childNode = new xmlNode(tagData.tagName);
216
+ childNode.add(this.options.textNodeName, "");
217
+
218
+ if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
219
+ childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath);
220
+ }
221
+ currentNode.addChild(childNode);
215
222
 
216
- const childNode = new xmlNode(tagData.tagName);
217
- childNode.add(this.options.textNodeName, "");
218
-
219
- if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
220
- childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath);
221
223
  }
222
- currentNode.addChild(childNode);
224
+
223
225
 
224
226
  i = tagData.closeIndex + 1;
225
227
  } else if(xmlData.substr(i + 1, 3) === '!--') {
226
- const endIndex = findClosingIndex(xmlData, "-->", i, "Comment is not closed.")
228
+ const endIndex = findClosingIndex(xmlData, "-->", i+4, "Comment is not closed.")
227
229
  if(this.options.commentPropName){
228
230
  const comment = xmlData.substring(i + 4, endIndex - 2);
229
231
 
@@ -266,14 +268,7 @@ const parseXml = function(xmlData) {
266
268
  if (currentNode && textData) {
267
269
  if(currentNode.tagname !== '!xml'){
268
270
  //when nested tag is found
269
- textData = this.parseTextData(textData
270
- , currentNode.tagname
271
- , jPath
272
- , false
273
- , currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false
274
- , false);
275
- if(textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData);
276
- textData = "";
271
+ textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
277
272
  }
278
273
  }
279
274
 
@@ -306,6 +301,10 @@ const parseXml = function(xmlData) {
306
301
  if(tagName !== tagExp && attrExpPresent){
307
302
  childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
308
303
  }
304
+ if(tagContent) {
305
+ tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
306
+ }
307
+
309
308
  jPath = jPath.substr(0, jPath.lastIndexOf("."));
310
309
  childNode.add(this.options.textNodeName, tagContent);
311
310
 
@@ -369,14 +368,16 @@ const replaceEntitiesValue = function(val){
369
368
  }
370
369
  return val;
371
370
  }
372
- function saveTextToParentTag(textData, currentNode, jPath) {
371
+ function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) {
373
372
  if (textData) { //store previously collected data as textNode
373
+ if(isLeafNode === undefined) isLeafNode = Object.keys(currentNode.child).length === 0
374
+
374
375
  textData = this.parseTextData(textData,
375
376
  currentNode.tagname,
376
377
  jPath,
377
378
  false,
378
379
  currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false,
379
- Object.keys(currentNode.child).length === 0);
380
+ isLeafNode);
380
381
 
381
382
  if (textData !== undefined && textData !== "")
382
383
  currentNode.add(this.options.textNodeName, textData);