Permalink
Browse files

Merge pull request #46 from salsita/master

Support for schema-driven generation
  • Loading branch information...
2 parents 14a0064 + b023b38 commit af802e2aeb65d218961f9c85c5ffd398f07f8254 @Leonidas-from-XIV committed Mar 2, 2012
Showing with 116 additions and 17 deletions.
  1. +45 −10 lib/xml2js.js
  2. +18 −1 src/xml2js.coffee
  3. +14 −6 test/fixtures/sample.xml
  4. +39 −0 test/xml2js.test.coffee
View
Oops, something went wrong.
View
@@ -5,6 +5,10 @@ events = require 'events'
isEmpty = (thing) ->
return typeof thing is "object" && thing? && Object.keys(thing).length is 0
+class exports.ValidationError extends Error
+ constructor: (message) ->
+ @message = message
+
class exports.Parser extends events.EventEmitter
constructor: (opts) ->
# default options. for compatibility's sake set to some
@@ -25,6 +29,8 @@ class exports.Parser extends events.EventEmitter
# merge attributes and child elements onto parent object. this may
# cause collisions.
mergeAttrs: false
+ # optional validator for schema-driven generawtion
+ validator: null
# overwrite them with the specified options, if any
@options[key] = value for own key, value of opts
@@ -95,6 +101,10 @@ class exports.Parser extends events.EventEmitter
if @options.emptyTag != undefined && isEmpty obj
obj = @options.emptyTag
+ if @options.validator?
+ xpath = "/" + (node["#name"] for node in stack).concat(nodeName).join("/")
+ obj = @options.validator(xpath, s and s[nodeName], obj)
+
# check whether we closed all the open tags
if stack.length > 0
if not @options.explicitArray
@@ -139,4 +149,11 @@ class exports.Parser extends events.EventEmitter
@emit "end", null
return true
- @saxParser.write str.toString()
+ try
+ @saxParser.write str.toString()
+ catch ex
+ if ex instanceof exports.ValidationError
+ @emit("error", ex.message)
+ else
+ throw ex
+
View
@@ -24,11 +24,19 @@
</arraytest>
<emptytest/>
<ordertest>
- <one>1</one>
- <two>2</two>
- <three>3</three>
- <one>4</one>
- <two>5</two>
- <three>6</three>
+ <one>1</one>
+ <two>2</two>
+ <three>3</three>
+ <one>4</one>
+ <two>5</two>
+ <three>6</three>
</ordertest>
+ <validatortest>
+ <emptyarray/>
+ <oneitemarray>
+ <item>Bar.</item>
+ </oneitemarray>
+ <numbertest>42</numbertest>
+ <stringtest>43</stringtest>
+ </validatortest>
</sample>
View
@@ -20,6 +20,25 @@ skeleton = (options, checks) ->
x2js.parseString data
else
x2js.parseString xmlString
+###
+The `validator` function validates the value at the XPath. It also transforms the value
+if necessary to conform to the schema or other validation information being used. If there
+is an existing value at this path it is supplied in `currentValue` (e.g. this is the second or
+later item in an array).
+If the validation fails it should throw a `ValidationError`.
+###
+validator = (xpath, currentValue, newValue) ->
+ if xpath == '/sample/validatortest/numbertest'
+ return Number(newValue)
+ else if xpath in ['/sample/arraytest', '/sample/validatortest/emptyarray', '/sample/validatortest/oneitemarray']
+ if not ('item' of newValue)
+ return {'item': []}
+ else if xpath in ['/sample/arraytest/item', '/sample/validatortest/emptyarray/item', '/sample/validatortest/oneitemarray/item']
+ if not currentValue
+ return [newValue]
+ else if xpath == '/validationerror'
+ throw new xml2js.ValidationError("Validation error!")
+ return newValue
module.exports =
'test parse with defaults': skeleton(undefined, (r) ->
@@ -152,3 +171,23 @@ module.exports =
assert.equal err, null
assert.equal r['chartest']['#'], 'Character data here!'
test.finish()
+
+ 'test validator': skeleton(validator: validator, (r) ->
+ assert.equal typeof r['validatortest']['stringtest'], 'string'
+ assert.equal typeof r['validatortest']['numbertest'], 'number'
+ assert.ok r['validatortest']['emptyarray']['item'] instanceof Array
+ assert.equal r['validatortest']['emptyarray']['item'].length, 0
+ assert.ok r['validatortest']['oneitemarray']['item'] instanceof Array
+ assert.equal r['validatortest']['oneitemarray']['item'].length, 1
+ assert.equal r['validatortest']['oneitemarray']['item'], 'Bar.'
+ assert.ok r['arraytest']['item'] instanceof Array
+ assert.equal r['arraytest']['item'].length, 2
+ assert.equal r['arraytest']['item'][0]['subitem'], 'Baz.'
+ assert.equal r['arraytest']['item'][1]['subitem'][0], 'Foo.'
+ assert.equal r['arraytest']['item'][1]['subitem'][1], 'Bar.')
+
+ 'test validation error': (test) ->
+ x2js = new xml2js.Parser({validator: validator})
+ x2js.parseString '<validationerror/>', (err, r) ->
+ assert.equal err, 'Validation error!'
+ test.finish()

0 comments on commit af802e2

Please sign in to comment.