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

NaturalIntelligence / fast-xml-parser / 4642859367

pending completion
4642859367

push

github

amit kumar gupta
update package detail

714 of 769 branches covered (92.85%)

2420 of 2465 relevant lines covered (98.17%)

192493.57 hits per line

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

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

5
const defaultOptions = {
1✔
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;
138✔
18
  },
19
  attributeValueProcessor: function(attrName, a) {
20
    return a;
81✔
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);
51✔
41
  if (this.options.ignoreAttributes || this.options.attributesGroupName) {
51✔
42
    this.isAttribute = function(/*a*/) {
23✔
43
      return false;
47✔
44
    };
45
  } else {
46
    this.attrPrefixLen = this.options.attributeNamePrefix.length;
28✔
47
    this.isAttribute = isAttribute;
28✔
48
  }
49

50
  this.processTextOrObjNode = processTextOrObjNode
51✔
51

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

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

78
Builder.prototype.j2x = function(jObj, level) {
1✔
79
  let attrStr = '';
102✔
80
  let val = '';
102✔
81
  for (let key in jObj) {
102✔
82
    if (typeof jObj[key] === 'undefined') {
178✔
83
      // supress undefined node
84
    } else if (jObj[key] === null) {
177✔
85
      if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
1!
86
      else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
1✔
87
      // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
88
    } else if (jObj[key] instanceof Date) {
176✔
89
      val += this.buildTextValNode(jObj[key], key, '', level);
2✔
90
    } else if (typeof jObj[key] !== 'object') {
174✔
91
      //premitive type
92
      const attr = this.isAttribute(key);
82✔
93
      if (attr) {
82✔
94
        attrStr += this.buildAttrPairStr(attr, '' + jObj[key]);
9✔
95
      }else {
96
        //tag value
97
        if (key === this.options.textNodeName) {
73✔
98
          let newval = this.options.tagValueProcessor(key, '' + jObj[key]);
19✔
99
          val += this.replaceEntitiesValue(newval);
19✔
100
        } else {
101
          val += this.buildTextValNode(jObj[key], key, '', level);
54✔
102
        }
103
      }
104
    } else if (Array.isArray(jObj[key])) {
92✔
105
      //repeated nodes
106
      const arrLen = jObj[key].length;
15✔
107
      let listTagVal = "";
15✔
108
      for (let j = 0; j < arrLen; j++) {
15✔
109
        const item = jObj[key][j];
32✔
110
        if (typeof item === 'undefined') {
32✔
111
          // supress undefined node
112
        } else if (item === null) {
31!
113
          if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
×
114
          else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
×
115
          // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
116
        } else if (typeof item === 'object') {
31✔
117
          if(this.options.oneListGroup ){
12✔
118
            listTagVal += this.j2x(item, level + 1).val;
2✔
119
          }else{
120
            listTagVal += this.processTextOrObjNode(item, key, level)
10✔
121
          }
122
        } else {
123
          listTagVal += this.buildTextValNode(item, key, '', level);
19✔
124
        }
125
      }
126
      if(this.options.oneListGroup){
15✔
127
        listTagVal = this.buildObjectNode(listTagVal, key, '', level);
1✔
128
      }
129
      val += listTagVal;
15✔
130
    } else {
131
      //nested node
132
      if (this.options.attributesGroupName && key === this.options.attributesGroupName) {
77✔
133
        const Ks = Object.keys(jObj[key]);
16✔
134
        const L = Ks.length;
16✔
135
        for (let j = 0; j < L; j++) {
16✔
136
          attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]);
26✔
137
        }
138
      } else {
139
        val += this.processTextOrObjNode(jObj[key], key, level)
61✔
140
      }
141
    }
142
  }
143
  return {attrStr: attrStr, val: val};
102✔
144
};
145

146
Builder.prototype.buildAttrPairStr = function(attrName, val){
1✔
147
  val = this.options.attributeValueProcessor(attrName, '' + val);
35✔
148
  val = this.replaceEntitiesValue(val);
35✔
149
  if (this.options.suppressBooleanAttributes && val === "true") {
35✔
150
    return ' ' + attrName;
3✔
151
  } else return ' ' + attrName + '="' + val + '"';
32✔
152
}
153

154
function processTextOrObjNode (object, key, level) {
155
  const result = this.j2x(object, level + 1);
71✔
156
  if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
71✔
157
    return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level);
5✔
158
  } else {
159
    return this.buildObjectNode(result.val, key, result.attrStr, level);
66✔
160
  }
161
}
162

163
Builder.prototype.buildObjectNode = function(val, key, attrStr, level) {
1✔
164
  if(val === ""){
67✔
165
    if(key[0] === "?") return  this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
10✔
166
    else {
167
      return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
8✔
168
    }
169
  }else{
170

171
    let tagEndExp = '</' + key + this.tagEndChar;
57✔
172
    let piClosingChar = "";
57✔
173
    
174
    if(key[0] === "?") {
57!
175
      piClosingChar = "?";
×
176
      tagEndExp = "";
×
177
    }
178
  
179
    if (attrStr && val.indexOf('<') === -1) {
57✔
180
      return ( this.indentate(level) + '<' +  key + attrStr + piClosingChar + '>' + val + tagEndExp );
5✔
181
    } else if (this.options.commentPropName !== false && key === this.options.commentPropName && piClosingChar.length === 0) {
52!
182
      return this.indentate(level) + `<!--${val}-->` + this.newLine;
×
183
    }else {
184
      return (
52✔
185
        this.indentate(level) + '<' + key + attrStr + piClosingChar + this.tagEndChar +
186
        val +
187
        this.indentate(level) + tagEndExp    );
188
    }
189
  }
190
}
191

192
Builder.prototype.closeTag = function(key){
1✔
193
  let closeTag = "";
21✔
194
  if(this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
21✔
195
    if(!this.options.suppressUnpairedNode) closeTag = "/"
7✔
196
  }else if(this.options.suppressEmptyNode){ //empty
14✔
197
    closeTag = "/";
7✔
198
  }else{
199
    closeTag = `></${key}`
7✔
200
  }
201
  return closeTag;
21✔
202
}
203

204
function buildEmptyObjNode(val, key, attrStr, level) {
205
  if (val !== '') {
×
206
    return this.buildObjectNode(val, key, attrStr, level);
×
207
  } else {
208
    if(key[0] === "?") return  this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
×
209
    else {
210
      return  this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
×
211
      // return this.buildTagStr(level,key, attrStr);
212
    }
213
  }
214
}
215

216
Builder.prototype.buildTextValNode = function(val, key, attrStr, level) {
1✔
217
  if (this.options.cdataPropName !== false && key === this.options.cdataPropName) {
80✔
218
    return this.indentate(level) + `<![CDATA[${val}]]>` +  this.newLine;
4✔
219
  }else if (this.options.commentPropName !== false && key === this.options.commentPropName) {
76✔
220
    return this.indentate(level) + `<!--${val}-->` +  this.newLine;
4✔
221
  }else if(key[0] === "?") {//PI tag
72!
222
    return  this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar; 
×
223
  }else{
224
    let textValue = this.options.tagValueProcessor(key, val);
72✔
225
    textValue = this.replaceEntitiesValue(textValue);
72✔
226
  
227
    if( textValue === ''){
72✔
228
      return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
13✔
229
    }else{
230
      return this.indentate(level) + '<' + key + attrStr + '>' +
59✔
231
         textValue +
232
        '</' + key + this.tagEndChar;
233
    }
234
  }
235
}
236

237
Builder.prototype.replaceEntitiesValue = function(textValue){
1✔
238
  if(textValue && textValue.length > 0 && this.options.processEntities){
126✔
239
    for (let i=0; i<this.options.entities.length; i++) {
78✔
240
      const entity = this.options.entities[i];
390✔
241
      textValue = textValue.replace(entity.regex, entity.val);
390✔
242
    }
243
  }
244
  return textValue;
126✔
245
}
246

247
function indentate(level) {
248
  return this.options.indentBy.repeat(level);
69✔
249
}
250

251
function isAttribute(name /*, options*/) {
252
  if (name.startsWith(this.options.attributeNamePrefix)) {
35✔
253
    return name.substr(this.attrPrefixLen);
9✔
254
  } else {
255
    return false;
26✔
256
  }
257
}
258

259
module.exports = Builder;
1✔
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