Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,43 @@ JavaScript:
}
```

## Force array to provide uniform structer

Useful when have to handle elements with array of items that sometimes have one element inside.

XML:

```xml
<xml>
<foo bar="BAR">
BAZ
</foo>
</xml>
```

Code:

```js
fromXml(xml, {forceArray:true});
```

JavaScript:

```json
{
"xml": [
{
"foo": [
{
"@bar": "BAR",
"#": "BAZ"
}
]
}
]
}
```

## CLI

```sh
Expand Down
2 changes: 1 addition & 1 deletion dist/from-xml.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 21 additions & 13 deletions from-xml.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
*
* @function fromXML
* @param text {String} The string to parse as XML
* @param [reviver] {Function} If a function, prescribes how the value
* @param [options.reviver] {Function} If a function, prescribes how the value
* originally produced by parsing is transformed, before being returned.
* @param [options.forceArray] {Boolean} = false If true, all childs will be always array event if contains one node.
* @param [options] {Function|Object} If function -> @see param [options.reviver]
* @returns {Object}
*/

Expand All @@ -25,8 +27,14 @@ var fromXML;

exports.fromXML = fromXML = _fromXML;

function _fromXML(text, reviver) {
return toObject(parseXML(text), reviver);
function _fromXML(text, options) {
if(typeof options === 'function' ) {
options = { reviver: options };
}

options = Object.assign({}, { forceArray: false }, options);

return toObject(parseXML(text), options.reviver, options.forceArray);
}

function parseXML(text) {
Expand Down Expand Up @@ -109,7 +117,7 @@ var fromXML;
return elem;
}

function parseAttribute(elem, reviver) {
function parseAttribute(elem, reviver, forceArray) {
if (!elem.t) return;
var list = elem.t.split(/([^\s='"]+(?:\s*=\s*(?:'[\S\s]*?'|"[\S\s]*?"|[^\s'"]*))?)/);
var length = list.length;
Expand Down Expand Up @@ -145,7 +153,7 @@ var fromXML;
if (reviver) {
val = reviver(str, val);
}
addObject(attributes, str, val);
addObject(attributes, str, val, forceArray);
}

return attributes;
Expand All @@ -165,13 +173,13 @@ var fromXML;
});
}

function toObject(elem, reviver) {
function toObject(elem, reviver, forceArray) {
if ("string" === typeof elem) return elem;

var raw = elem.r;
if (raw) return raw;

var attributes = parseAttribute(elem, reviver);
var attributes = parseAttribute(elem, reviver, false);
var object;
var childList = elem.f;
var childLength = childList.length;
Expand All @@ -181,18 +189,18 @@ var fromXML;
object = attributes || {};
childList.forEach(function(child) {
if ("string" === typeof child) {
addObject(object, CHILD_NODE_KEY, child);
addObject(object, CHILD_NODE_KEY, child, forceArray);
} else {
addObject(object, child.n, toObject(child, reviver));
addObject(object, child.n, toObject(child, reviver, forceArray), forceArray);
}
});
} else if (childLength) {
// the node has single child node but no attribute
var child = childList[0];
object = toObject(child, reviver);
object = toObject(child, reviver, forceArray);
if (child.n) {
var wrap = {};
wrap[child.n] = object;
wrap[child.n] = forceArray ? [object] : object;
object = wrap;
}
} else {
Expand All @@ -207,15 +215,15 @@ var fromXML;
return object;
}

function addObject(object, key, val) {
function addObject(object, key, val, forceArray) {
if ("undefined" === typeof val) return;
var prev = object[key];
if (prev instanceof Array) {
prev.push(val);
} else if (key in object) {
object[key] = [prev, val];
} else {
object[key] = val;
object[key] = forceArray && key !== "#" ? [val] : val;
}
}
})(typeof exports === "object" && exports || {});
32 changes: 31 additions & 1 deletion test/10.from-xml.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,4 +349,34 @@ describe('fromXML', function() {
assert.deepEqual(fromXML('<foo><bar>BAR</BAR><baz>BAZ</BAZ></FOO>'),
{"foo": {"bar": "BAR", "baz": "BAZ"}});
});
});

describe('with forceArray flag', function() {
it('multiple attributes', function () {
assert.deepEqual(fromXML('<foo bar="BAR" baz="BAZ"/>', {forceArray:true}),
{foo: [{"@bar": "BAR", "@baz": "BAZ"}]});
assert.deepEqual(fromXML('<foo bar="BAR" baz="BAZ"></foo>', {forceArray:true}),
{foo: [{"@bar": "BAR", "@baz": "BAZ"}]});
assert.deepEqual(fromXML('<foo bar baz></foo>', {forceArray:true}),
{foo: [{"@bar": null, "@baz": null}]});
assert.deepEqual(fromXML('<foo bar="BAR" bar="BAZ"></foo>', {forceArray:true}),
{foo: [{"@bar": ["BAR", "BAZ"]}]});
assert.deepEqual(fromXML('<foo bar bar></foo>', {forceArray:true}),
{foo: [{"@bar": [null, null]}]});
});

it('attributes and child elements', function () {
assert.deepEqual(fromXML('<foo bar="BAR">FOO</foo>', {forceArray:true}),
{foo: [{"@bar": "BAR", "#": "FOO"}]});
assert.deepEqual(fromXML('<foo bar="BAR" baz="BAZ">FOO</foo>', {forceArray:true}),
{foo: [{"@bar": "BAR", "@baz": "BAZ", "#": "FOO"}]});
assert.deepEqual(fromXML('<foo bar="BAR" baz="BAZ"><qux>QUX</qux></foo>', {forceArray:true}),
{foo: [{"@bar": "BAR", "@baz": "BAZ", "qux": ["QUX"]}]});
assert.deepEqual(fromXML('<foo bar="BAR" baz="BAZ"><qux>QUX</qux>QUUX</foo>', {forceArray:true}),
{foo: [{"@bar": "BAR", "@baz": "BAZ", "qux": ["QUX"], "#": "QUUX"}]});
assert.deepEqual(fromXML('<foo bar="BAR" baz="BAZ"><qux>QUX</qux><quux>QUUX</quux></foo>', {forceArray:true}),
{foo: [{"@bar": "BAR", "@baz": "BAZ", "qux": ["QUX"], "quux": ["QUUX"]}]});
assert.deepEqual(fromXML('<foo bar="BAR" baz="BAZ"><qux>QUX</qux><qux>QUX2</qux><quux>QUUX</quux></foo>', {forceArray:true}),
{foo: [{"@bar": "BAR", "@baz": "BAZ", "qux": ["QUX", "QUX2"], "quux": ["QUUX"]}]});
});
})
});