• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NaturalIntelligence / fast-xml-parser / 6243870127

20 Sep 2023 04:06AM UTC coverage: 98.248% (+0.009%) from 98.239%
6243870127

push

github

amitguptagwl
update release detail

773 of 831 branches covered (0.0%)

2635 of 2682 relevant lines covered (98.25%)

530795.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.67
/src/xmlbuilder/json2xml.js
1
'use strict';
2
//parse Empty Node as self closing node
3
const buildFromOrderedJs = require('./orderedJs2Xml');
3✔
4

5
const defaultOptions = {
3✔
6
  attributeNamePrefix: '@_',
7
  attributesGroupName: false,
8
  textNodeName: '#text',
9
  ignoreAttributes: true,
10
  cdataPropName: false,
11
  format: false,
12
  indentBy: '  ',
13
  suppressEmptyNode: false,
14
  suppressUnpairedNode: true,
15
  suppressBooleanAttributes: true,
16
  tagValueProcessor: function(key, a) {
17
    return a;
516✔
18
  },
19
  attributeValueProcessor: function(attrName, a) {
20
    return a;
303✔
21
  },
22
  preserveOrder: false,
23
  commentPropName: false,
24
  unpairedTags: [],
25
  entities: [
26
    { regex: new RegExp("&", "g"), val: "&" },//it must be on top
27
    { regex: new RegExp(">", "g"), val: ">" },
28
    { regex: new RegExp("<", "g"), val: "&lt;" },
29
    { regex: new RegExp("\'", "g"), val: "&apos;" },
30
    { regex: new RegExp("\"", "g"), val: "&quot;" }
31
  ],
32
  processEntities: true,
33
  stopNodes: [],
34
  // transformTagName: false,
35
  // transformAttributeName: false,
36
  oneListGroup: false
37
};
38

39
function Builder(options) {
40
  this.options = Object.assign({}, defaultOptions, options);
171✔
41
  if (this.options.ignoreAttributes || this.options.attributesGroupName) {
171✔
42
    this.isAttribute = function(/*a*/) {
72✔
43
      return false;
150✔
44
    };
45
  } else {
46
    this.attrPrefixLen = this.options.attributeNamePrefix.length;
99✔
47
    this.isAttribute = isAttribute;
99✔
48
  }
49

50
  this.processTextOrObjNode = processTextOrObjNode
171✔
51

52
  if (this.options.format) {
171✔
53
    this.indentate = indentate;
63✔
54
    this.tagEndChar = '>\n';
63✔
55
    this.newLine = '\n';
63✔
56
  } else {
57
    this.indentate = function() {
108✔
58
      return '';
465✔
59
    };
60
    this.tagEndChar = '>';
108✔
61
    this.newLine = '';
108✔
62
  }
63
}
64

65
Builder.prototype.build = function(jObj) {
3✔
66
  if(this.options.preserveOrder){
171✔
67
    return buildFromOrderedJs(jObj, this.options);
66✔
68
  }else {
69
    if(Array.isArray(jObj) && this.options.arrayNodeName && this.options.arrayNodeName.length > 1){
105✔
70
      jObj = {
3✔
71
        [this.options.arrayNodeName] : jObj
72
      }
73
    }
74
    return this.j2x(jObj, 0).val;
105✔
75
  }
76
};
77

78
Builder.prototype.j2x = function(jObj, level) {
3✔
79
  let attrStr = '';
417✔
80
  let val = '';
417✔
81
  for (let key in jObj) {
417✔
82
    if(!jObj.hasOwnProperty(key)) continue;
1,167✔
83
    if (typeof jObj[key] === 'undefined') {
765✔
84
      // supress undefined node only if it is not an attribute
85
      if (this.isAttribute(key)) {
33✔
86
        val += '';
30✔
87
      }
88
    } else if (jObj[key] === null) {
732✔
89
      // null attribute should be ignored by the attribute list, but should not cause the tag closing
90
      if (this.isAttribute(key)) {
33✔
91
        val += '';
30✔
92
      } else if (key[0] === '?') {
3!
93
        val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
×
94
      } else {
95
        val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
3✔
96
      }
97
      // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
98
    } else if (jObj[key] instanceof Date) {
699✔
99
      val += this.buildTextValNode(jObj[key], key, '', level);
6✔
100
    } else if (typeof jObj[key] !== 'object') {
693✔
101
      //premitive type
102
      const attr = this.isAttribute(key);
384✔
103
      if (attr) {
384✔
104
        attrStr += this.buildAttrPairStr(attr, '' + jObj[key]);
87✔
105
      }else {
106
        //tag value
107
        if (key === this.options.textNodeName) {
297✔
108
          let newval = this.options.tagValueProcessor(key, '' + jObj[key]);
132✔
109
          val += this.replaceEntitiesValue(newval);
132✔
110
        } else {
111
          val += this.buildTextValNode(jObj[key], key, '', level);
165✔
112
        }
113
      }
114
    } else if (Array.isArray(jObj[key])) {
309✔
115
      //repeated nodes
116
      const arrLen = jObj[key].length;
57✔
117
      let listTagVal = "";
57✔
118
      for (let j = 0; j < arrLen; j++) {
57✔
119
        const item = jObj[key][j];
180✔
120
        if (typeof item === 'undefined') {
180✔
121
          // supress undefined node
122
        } else if (item === null) {
177!
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;
126
        } else if (typeof item === 'object') {
177✔
127
          if(this.options.oneListGroup ){
108✔
128
            listTagVal += this.j2x(item, level + 1).val;
6✔
129
          }else{
130
            listTagVal += this.processTextOrObjNode(item, key, level)
102✔
131
          }
132
        } else {
133
          listTagVal += this.buildTextValNode(item, key, '', level);
69✔
134
        }
135
      }
136
      if(this.options.oneListGroup){
57✔
137
        listTagVal = this.buildObjectNode(listTagVal, key, '', level);
3✔
138
      }
139
      val += listTagVal;
57✔
140
    } else {
141
      //nested node
142
      if (this.options.attributesGroupName && key === this.options.attributesGroupName) {
252✔
143
        const Ks = Object.keys(jObj[key]);
48✔
144
        const L = Ks.length;
48✔
145
        for (let j = 0; j < L; j++) {
48✔
146
          attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]);
78✔
147
        }
148
      } else {
149
        val += this.processTextOrObjNode(jObj[key], key, level)
204✔
150
      }
151
    }
152
  }
153
  return {attrStr: attrStr, val: val};
417✔
154
};
155

156
Builder.prototype.buildAttrPairStr = function(attrName, val){
3✔
157
  val = this.options.attributeValueProcessor(attrName, '' + val);
165✔
158
  val = this.replaceEntitiesValue(val);
165✔
159
  if (this.options.suppressBooleanAttributes && val === "true") {
165✔
160
    return ' ' + attrName;
9✔
161
  } else return ' ' + attrName + '="' + val + '"';
156✔
162
}
163

164
function processTextOrObjNode (object, key, level) {
165
  const result = this.j2x(object, level + 1);
306✔
166
  if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
306✔
167
    return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level);
27✔
168
  } else {
169
    return this.buildObjectNode(result.val, key, result.attrStr, level);
279✔
170
  }
171
}
172

173
Builder.prototype.buildObjectNode = function(val, key, attrStr, level) {
3✔
174
  if(val === ""){
282✔
175
    if(key[0] === "?") return  this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
30✔
176
    else {
177
      return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
24✔
178
    }
179
  }else{
180

181
    let tagEndExp = '</' + key + this.tagEndChar;
252✔
182
    let piClosingChar = "";
252✔
183
    
184
    if(key[0] === "?") {
252!
185
      piClosingChar = "?";
×
186
      tagEndExp = "";
×
187
    }
188
  
189
    // attrStr is an empty string in case the attribute came as undefined or null
190
    if ((attrStr || attrStr === '') && val.indexOf('<') === -1) {
252✔
191
      return ( this.indentate(level) + '<' +  key + attrStr + piClosingChar + '>' + val + tagEndExp );
78✔
192
    } else if (this.options.commentPropName !== false && key === this.options.commentPropName && piClosingChar.length === 0) {
174!
193
      return this.indentate(level) + `<!--${val}-->` + this.newLine;
×
194
    }else {
195
      return (
174✔
196
        this.indentate(level) + '<' + key + attrStr + piClosingChar + this.tagEndChar +
197
        val +
198
        this.indentate(level) + tagEndExp    );
199
    }
200
  }
201
}
202

203
Builder.prototype.closeTag = function(key){
3✔
204
  let closeTag = "";
63✔
205
  if(this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
63✔
206
    if(!this.options.suppressUnpairedNode) closeTag = "/"
21✔
207
  }else if(this.options.suppressEmptyNode){ //empty
42✔
208
    closeTag = "/";
21✔
209
  }else{
210
    closeTag = `></${key}`
21✔
211
  }
212
  return closeTag;
63✔
213
}
214

215
function buildEmptyObjNode(val, key, attrStr, level) {
216
  if (val !== '') {
×
217
    return this.buildObjectNode(val, key, attrStr, level);
×
218
  } else {
219
    if(key[0] === "?") return  this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
×
220
    else {
221
      return  this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
×
222
      // return this.buildTagStr(level,key, attrStr);
223
    }
224
  }
225
}
226

227
Builder.prototype.buildTextValNode = function(val, key, attrStr, level) {
3✔
228
  if (this.options.cdataPropName !== false && key === this.options.cdataPropName) {
267✔
229
    return this.indentate(level) + `<![CDATA[${val}]]>` +  this.newLine;
12✔
230
  }else if (this.options.commentPropName !== false && key === this.options.commentPropName) {
255✔
231
    return this.indentate(level) + `<!--${val}-->` +  this.newLine;
12✔
232
  }else if(key[0] === "?") {//PI tag
243!
233
    return  this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar; 
×
234
  }else{
235
    let textValue = this.options.tagValueProcessor(key, val);
243✔
236
    textValue = this.replaceEntitiesValue(textValue);
243✔
237
  
238
    if( textValue === ''){
243✔
239
      return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
39✔
240
    }else{
241
      return this.indentate(level) + '<' + key + attrStr + '>' +
204✔
242
         textValue +
243
        '</' + key + this.tagEndChar;
244
    }
245
  }
246
}
247

248
Builder.prototype.replaceEntitiesValue = function(textValue){
3✔
249
  if(textValue && textValue.length > 0 && this.options.processEntities){
540✔
250
    for (let i=0; i<this.options.entities.length; i++) {
393✔
251
      const entity = this.options.entities[i];
1,965✔
252
      textValue = textValue.replace(entity.regex, entity.val);
1,965✔
253
    }
254
  }
255
  return textValue;
540✔
256
}
257

258
function indentate(level) {
259
  return this.options.indentBy.repeat(level);
261✔
260
}
261

262
function isAttribute(name /*, options*/) {
263
  if (name.startsWith(this.options.attributeNamePrefix) && name !== this.options.textNodeName) {
300✔
264
    return name.substr(this.attrPrefixLen);
147✔
265
  } else {
266
    return false;
153✔
267
  }
268
}
269

270
module.exports = Builder;
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc