Skip to content
This repository was archived by the owner on Jan 7, 2021. It is now read-only.

Commit 8a4a66d

Browse files
authored
Merge pull request #134 from LaughingBubba/alt-text-node
alternate text node
2 parents 555ff88 + 34d9f9a commit 8a4a66d

9 files changed

+120
-12
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,15 @@ var options = {
5454
sanitize: true,
5555
trim: true,
5656
arrayNotation: false
57+
alternateTextNode: false
5758
};
5859
```
5960

6061
* **object:** Returns a Javascript object instead of a JSON string
6162
* **reversible:** Makes the JSON reversible to XML (*)
6263
* **coerce:** Makes type coercion. i.e.: numbers and booleans present in attributes and element values are converted from string to its correspondent data types. Coerce can be optionally defined as an object with specific methods of coercion based on attribute name or tag name, with fallback to default coercion.
6364
* **trim:** Removes leading and trailing whitespaces as well as line terminators in element values.
64-
* **arrayNotation:** XML child nodes are always treated as arrays
65+
* **arrayNotation:** XML child nodes are always treated as arrays NB: you can specify a selective array of nodes for this to apply to instead of the whole document.
6566
* **sanitize:** Sanitizes the following characters present in element values:
6667

6768
```javascript
@@ -76,6 +77,8 @@ var chars = {
7677
"'": '''
7778
};
7879
```
80+
* **alternateTextNode:** Changes the default textNode property from $t to _t when option is set to true. Alternatively a string can be specified which will override $t to what ever the string is.
81+
7982

8083
### Options object for `toXml`
8184

lib/xml2json.js

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,34 +51,34 @@ function startElement(name, attrs) {
5151
}
5252

5353
function text(data) {
54-
currentObject['$t'] = (currentObject['$t'] || '') + data;
54+
currentObject[textNodeName()] = (currentObject[textNodeName()] || '') + data;
5555
}
5656

5757
function endElement(name) {
58-
if (currentObject['$t']) {
58+
if (currentObject[textNodeName()]) {
5959
if (options.trim) {
60-
currentObject['$t'] = currentObject['$t'].trim()
60+
currentObject[textNodeName()] = currentObject[textNodeName()].trim()
6161
}
6262

6363
if (options.sanitize) {
64-
currentObject['$t'] = sanitizer.sanitize(currentObject['$t'], true);
64+
currentObject[textNodeName()] = sanitizer.sanitize(currentObject[textNodeName()], true);
6565
}
6666

67-
currentObject['$t'] = coerce(currentObject['$t'],name);
67+
currentObject[textNodeName()] = coerce(currentObject[textNodeName()],name);
6868
}
6969

7070
if (currentElementName !== name) {
71-
delete currentObject['$t'];
71+
delete currentObject[textNodeName()];
7272
}
7373
// This should check to make sure that the name we're ending
7474
// matches the name we started on.
7575
var ancestor = ancestors.pop();
7676
if (!options.reversible) {
77-
if (('$t' in currentObject) && (Object.keys(currentObject).length == 1)) {
77+
if ((textNodeName() in currentObject) && (Object.keys(currentObject).length == 1)) {
7878
if (ancestor[name] instanceof Array) {
79-
ancestor[name].push(ancestor[name].pop()['$t']);
79+
ancestor[name].push(ancestor[name].pop()[textNodeName()]);
8080
} else {
81-
ancestor[name] = currentObject['$t'];
81+
ancestor[name] = currentObject[textNodeName()];
8282
}
8383
}
8484
}
@@ -112,6 +112,10 @@ function coerce(value,key) {
112112
return value;
113113
}
114114

115+
function textNodeName() {
116+
return options.alternateTextNode ? typeof options.alternateTextNode === 'string' ? options.alternateTextNode : '_t' : '$t'
117+
}
118+
115119

116120
/**
117121
* Parses xml to json using node-expat.
@@ -124,6 +128,10 @@ function coerce(value,key) {
124128
* characterized by the presence of the property $t.
125129
* - sanitize_values: If true, the parser escapes any element value in the xml
126130
* that has any of the following characters: <, >, (, ), #, #, &, ", '.
131+
* - alternateTextNode (boolean OR string):
132+
* If false or not specified: default of $t is used
133+
* If true, whenever $t is returned as an end point, is is substituted with _t
134+
* it String, whenever $t is returned as an end point, is is substituted with the String value (care advised)
127135
*
128136
* @return {String|Object} A String or an Object with the JSON representation
129137
* of the XML.
@@ -147,7 +155,8 @@ module.exports = function(xml, _options) {
147155
coerce: joi.alternatives([joi.boolean(), joi.object()]).default(false),
148156
sanitize: joi.boolean().default(true),
149157
trim: joi.boolean().default(true),
150-
arrayNotation: joi.alternatives([joi.boolean(), joi.array()]).default(false)
158+
arrayNotation: joi.alternatives([joi.boolean(), joi.array()]).default(false),
159+
alternateTextNode: [joi.boolean().default(false), joi.string().default(false)]
151160
};
152161
var validation = joi.validate(_options, schema);
153162
hoek.assert(validation.error === null, validation.error);
@@ -171,6 +180,6 @@ module.exports = function(xml, _options) {
171180

172181
//See: http://timelessrepo.com/json-isnt-a-javascript-subset
173182
json = json.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
174-
183+
175184
return json;
176185
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"unit":{"test":{"case":[{"justText":{"$t":"blah blah"}},{"attribText":{"attrib":"das","$t":"capital"}}]}}}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<unit>
2+
<test>
3+
<case>
4+
<justText>blah blah</justText>
5+
</case>
6+
<case>
7+
<attribText attrib='das'>capital
8+
</attribText>
9+
</case>
10+
</test>
11+
</unit>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"unit":{"test":{"case":[{"justText":{"_t":"blah blah"}},{"attribText":{"attrib":"das","_t":"capital"}}]}}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"unit":{"test":{"case":[{"justText":{"xx":"blah blah"}},{"attribText":{"attrib":"das","xx":"capital"}}]}}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"unit":{"test":{"case":[{"justText":{"zz":"blah blah"}},{"attribText":{"attrib":"das","zz":"capital"}},{"spaces":{"zz":"abc\nasdf\n a"}},{"type":"SANATISE","san":{"b":"Smith & Son","zz":"Alpha & Omega"}}]}}}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<unit>
2+
<test>
3+
<case>
4+
<justText>blah blah</justText>
5+
</case>
6+
<case>
7+
<attribText attrib='das'>capital
8+
</attribText>
9+
</case>
10+
<case>
11+
<spaces> abc
12+
asdf
13+
a </spaces>
14+
</case>
15+
<case type="SANATISE">
16+
<san b="Smith &amp; Son">
17+
Alpha &amp; Omega
18+
</san>
19+
</case>
20+
</test>
21+
</unit>
22+

test/test.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,65 @@ describe('xml2json', function () {
170170
});
171171
})
172172

173+
describe('alternateTextNode', function () {
174+
175+
it('A1: defaults without the option being defined', function(done) {
176+
177+
var xml = internals.readFixture('alternate-text-node-A.xml');
178+
var result = parser.toJson(xml, {reversible: true});
179+
var json = internals.readFixture('alternate-text-node-A.json');
180+
181+
expect(result).to.equal(json);
182+
183+
done();
184+
});
185+
186+
it('A2: defaults with option as false', function(done) {
187+
188+
var xml = internals.readFixture('alternate-text-node-A.xml');
189+
var result = parser.toJson(xml, {alternateTextNode: false, reversible: true});
190+
var json = internals.readFixture('alternate-text-node-A.json');
191+
192+
expect(result).to.equal(json);
193+
194+
done();
195+
});
196+
197+
198+
it('B: uses alternate text node with option as true', function(done) {
199+
200+
var xml = internals.readFixture('alternate-text-node-A.xml');
201+
var result = parser.toJson(xml, {alternateTextNode: true, reversible: true});
202+
var json = internals.readFixture('alternate-text-node-B.json');
203+
204+
expect(result).to.equal(json);
205+
206+
done();
207+
});
208+
209+
it('C: overrides text node with option as "xx" string', function(done) {
210+
211+
var xml = internals.readFixture('alternate-text-node-A.xml');
212+
var result = parser.toJson(xml, {alternateTextNode: "xx", reversible: true});
213+
var json = internals.readFixture('alternate-text-node-C.json');
214+
215+
expect(result).to.equal(json);
216+
217+
done();
218+
});
219+
220+
it('D: double check sanatize and trim', function (done) {
221+
222+
var xml = internals.readFixture('alternate-text-node-D.xml');
223+
var result = parser.toJson(xml, {alternateTextNode: "zz", sanitize: true, trim: true, reversible: true});
224+
var json = internals.readFixture('alternate-text-node-D.json');
225+
226+
expect(result).to.equal(json);
227+
228+
done();
229+
});
230+
231+
})
173232
});
174233

175234

0 commit comments

Comments
 (0)