Skip to content

Commit

Permalink
Merge branch 'ryedin-children_enhancements'
Browse files Browse the repository at this point in the history
Closes #31.
  • Loading branch information
Leonidas-from-XIV committed Apr 27, 2015
2 parents f7b9ae6 + 5419934 commit 7a89aaf
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 34 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,2 +1,3 @@
*.swp
node_modules
npm-debug.log
8 changes: 8 additions & 0 deletions README.md
Expand Up @@ -256,6 +256,14 @@ value})``. Possible options are:
then "children" won't be created. Added in 0.2.5.
* `childkey` (default `$$`): Prefix that is used to access child elements if
`explicitChildren` is set to `true`. Added in 0.2.5.
* `preserveChildrenOrder` (default `false`): Modifies the behavior of
`explicitChildren` so that the value of the "children" property becomes an
ordered array. When this is `true`, every node will also get a `#name` field
whose value will correspond to the XML nodeName, so that you may iterate
the "children" array and still be able to determine node names. The named
(and potentially unordered) properties are also retained in this
configuration at the same level as the ordered "children" array. Added in
0.4.9.
* `charsAsChildren` (default `false`): Determines whether chars should be
considered children if `explicitChildren` is on. Added in 0.2.5.
* `async` (default `false`): Should the callbacks be async? This *might* be
Expand Down
2 changes: 1 addition & 1 deletion lib/bom.js

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

2 changes: 1 addition & 1 deletion lib/processors.js

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

57 changes: 41 additions & 16 deletions lib/xml2js.js

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

3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -37,7 +37,8 @@
"Jess Telford <hi@jes.st> (http://jes.st)",
"Tom Hughes <<tom@compton.nu> (http://compton.nu/)",
"Piotr Rochala (http://rocha.la/)",
"Michael Avila (https://github.com/michaelavila)"
"Michael Avila (https://github.com/michaelavila)",
"Ryan Gahl (https://github.com/ryedin)"
],
"main" : "./lib/xml2js",
"directories" : {
Expand Down
52 changes: 37 additions & 15 deletions src/xml2js.coffee
Expand Up @@ -78,6 +78,7 @@ exports.defaults =
validator: null
xmlns : false
explicitChildren: false
preserveChildrenOrder: false
childkey: '$$'
charsAsChildren: false
# not async in 0.2 mode either
Expand Down Expand Up @@ -273,7 +274,7 @@ class exports.Parser extends events.EventEmitter
@saxParser.onclosetag = =>
obj = stack.pop()
nodeName = obj["#name"]
delete obj["#name"]
delete obj["#name"] if not @options.explicitChildren or not @options.preserveChildrenOrder

cdata = obj.cdata
delete obj.cdata
Expand Down Expand Up @@ -304,20 +305,33 @@ class exports.Parser extends events.EventEmitter

# put children into <childkey> property and unfold chars if necessary
if @options.explicitChildren and not @options.mergeAttrs and typeof obj is 'object'
node = {}
# separate attributes
if @options.attrkey of obj
node[@options.attrkey] = obj[@options.attrkey]
delete obj[@options.attrkey]
# separate char data
if not @options.charsAsChildren and @options.charkey of obj
node[@options.charkey] = obj[@options.charkey]
delete obj[@options.charkey]

if Object.getOwnPropertyNames(obj).length > 0
node[@options.childkey] = obj

obj = node
if not @options.preserveChildrenOrder
node = {}
# separate attributes
if @options.attrkey of obj
node[@options.attrkey] = obj[@options.attrkey]
delete obj[@options.attrkey]
# separate char data
if not @options.charsAsChildren and @options.charkey of obj
node[@options.charkey] = obj[@options.charkey]
delete obj[@options.charkey]

if Object.getOwnPropertyNames(obj).length > 0
node[@options.childkey] = obj

obj = node
else if s
# append current node onto parent's <childKey> array
s[@options.childkey] = s[@options.childkey] or []
# push a clone so that the node in the children array can receive the #name property while the original obj can do without it
objClone = {}
for own key of obj
objClone[key] = obj[key]
s[@options.childkey].push objClone
delete obj["#name"]
# re-check whether we can collapse the node now to just the charkey value
if Object.keys(obj).length == 1 and charkey of obj and not @EXPLICIT_CHARKEY
obj = obj[charkey]

# check whether we closed all the open tags
if stack.length > 0
Expand All @@ -340,6 +354,14 @@ class exports.Parser extends events.EventEmitter
s = stack[stack.length - 1]
if s
s[charkey] += text

if @options.explicitChildren and @options.preserveChildrenOrder and @options.charsAsChildren and text.replace(/\\n/g, '').trim() isnt ''
s[@options.childkey] = s[@options.childkey] or []
charChild =
'#name': '__text__'
charChild[charkey] = text
s[@options.childkey].push charChild

s

@saxParser.ontext = ontext
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/sample.xml
Expand Up @@ -53,4 +53,5 @@
<attrNameProcessTest camelCaseAttr="camelCaseAttrValue" lowercaseattr="lowercaseattr" />
<tagNameProcessTest/>
<valueProcessTest>some value</valueProcessTest>
<textordertest>this is text with <b>markup</b> in the middle</textordertest>
</sample>
41 changes: 41 additions & 0 deletions test/parser.test.coffee
Expand Up @@ -145,6 +145,47 @@ module.exports =
# determine number of items in object
equ Object.keys(r.sample.$$.tagcasetest[0].$$).length, 3)

'test parse with explicitChildren and preserveChildrenOrder': skeleton(explicitChildren: true, preserveChildrenOrder: true, (r) ->
console.log 'Result object: ' + util.inspect r, false, 10
equ r.sample.$$[10]['#name'], 'ordertest'
equ r.sample.$$[10].$$[0]['#name'], 'one'
equ r.sample.$$[10].$$[0]._, '1'
equ r.sample.$$[10].$$[1]['#name'], 'two'
equ r.sample.$$[10].$$[1]._, '2'
equ r.sample.$$[10].$$[2]['#name'], 'three'
equ r.sample.$$[10].$$[2]._, '3'
equ r.sample.$$[10].$$[3]['#name'], 'one'
equ r.sample.$$[10].$$[3]._, '4'
equ r.sample.$$[10].$$[4]['#name'], 'two'
equ r.sample.$$[10].$$[4]._, '5'
equ r.sample.$$[10].$$[5]['#name'], 'three'
equ r.sample.$$[10].$$[5]._, '6')

'test parse with explicitChildren and charsAsChildren and preserveChildrenOrder': skeleton(explicitChildren: true, preserveChildrenOrder: true, charsAsChildren: true, (r) ->
console.log 'Result object: ' + util.inspect r, false, 10
equ r.sample.$$[10]['#name'], 'ordertest'
equ r.sample.$$[10].$$[0]['#name'], 'one'
equ r.sample.$$[10].$$[0]._, '1'
equ r.sample.$$[10].$$[1]['#name'], 'two'
equ r.sample.$$[10].$$[1]._, '2'
equ r.sample.$$[10].$$[2]['#name'], 'three'
equ r.sample.$$[10].$$[2]._, '3'
equ r.sample.$$[10].$$[3]['#name'], 'one'
equ r.sample.$$[10].$$[3]._, '4'
equ r.sample.$$[10].$$[4]['#name'], 'two'
equ r.sample.$$[10].$$[4]._, '5'
equ r.sample.$$[10].$$[5]['#name'], 'three'
equ r.sample.$$[10].$$[5]._, '6'

# test text ordering with XML nodes in the middle
equ r.sample.$$[16]['#name'], 'textordertest'
equ r.sample.$$[16].$$[0]['#name'], '__text__'
equ r.sample.$$[16].$$[0]._, 'this is text with '
equ r.sample.$$[16].$$[1]['#name'], 'b'
equ r.sample.$$[16].$$[1]._, 'markup'
equ r.sample.$$[16].$$[2]['#name'], '__text__'
equ r.sample.$$[16].$$[2]._, ' in the middle')

'test element without children': skeleton(explicitChildren: true, (r) ->
console.log 'Result object: ' + util.inspect r, false, 10
equ r.sample.$$.nochildrentest[0].$$, undefined)
Expand Down

0 comments on commit 7a89aaf

Please sign in to comment.