1 var sax = require(
'sax')
2 , dateFormatter = require(
'./date_formatter')
4 var Deserializer = function(encoding) {
6 this.responseType = null
10 this.methodname = null
11 this.encoding = encoding ||
'utf8'
16 this.parser = sax.createStream()
17 this.parser.on(
'opentag', this.onOpentag.bind(
this))
18 this.parser.on(
'closetag',
this.onClosetag.bind(
this))
19 this.parser.on(
'text',
this.onText.bind(
this))
20 this.parser.on(
'cdata',
this.onCDATA.bind(
this))
21 this.parser.on(
'end',
this.onDone.bind(
this))
22 this.parser.on(
'error',
this.onError.bind(
this))
25 Deserializer.prototype.deserializeMethodResponse =
function(stream, callback) {
28 this.callback =
function(error, result) {
32 else if (result.length > 1) {
33 callback(
new Error(
'Response has more than one param'))
35 else if (that.type !==
'methodresponse') {
36 callback(
new Error(
'Not a method response'))
38 else if (!that.responseType) {
39 callback(
new Error(
'Invalid method response'))
42 callback(null, result[0])
46 stream.setEncoding(this.encoding)
47 stream.on(
'error', this.onError.bind(
this))
48 stream.pipe(
this.parser)
51 Deserializer.prototype.deserializeMethodCall =
function(stream, callback) {
54 this.callback =
function(error, result) {
58 else if (that.type !==
'methodcall') {
59 callback(
new Error(
'Not a method call'))
61 else if (!that.methodname) {
62 callback(
new Error(
'Method call did not contain a method name'))
65 callback(null, that.methodname, result)
69 stream.setEncoding(this.encoding)
70 stream.on(
'error', this.onError.bind(
this))
71 stream.pipe(
this.parser)
74 Deserializer.prototype.onDone =
function() {
78 if (this.type === null || this.marks.length) {
79 this.callback(
new Error(
'Invalid XML-RPC message'))
81 else if (this.responseType ===
'fault') {
82 var createFault =
function(fault) {
83 var error =
new Error(
'XML-RPC fault' + (fault.faultString ?
': ' + fault.faultString :
''))
84 error.code = fault.faultCode
85 error.faultCode = fault.faultCode
86 error.faultString = fault.faultString
89 this.callback(createFault(this.stack[0]))
92 this.callback(undefined, this.stack)
106 Deserializer.prototype.onError =
function(msg) {
108 if (typeof msg ===
'string') {
109 this.error =
new Error(msg)
114 this.callback(this.error)
118 Deserializer.prototype.push =
function(value) {
119 this.stack.push(value)
126 Deserializer.prototype.onOpentag =
function(node) {
127 if (node.name ===
'ARRAY' || node.name ===
'STRUCT') {
128 this.marks.push(this.stack.length)
131 this.value = (node.name ===
'VALUE')
134 Deserializer.prototype.onText =
function(text) {
138 Deserializer.prototype.onCDATA =
function(cdata) {
139 this.data.push(cdata)
142 Deserializer.prototype.onClosetag =
function(el) {
143 var data = this.data.join(
'')
147 this.endBoolean(data)
172 case 'DATETIME.ISO8601':
173 this.endDateTime(data)
184 case 'METHODRESPONSE':
185 this.endMethodResponse(data)
188 this.endMethodName(data)
191 this.endMethodCall(data)
202 this.onError('Unknown XML-RPC tag \'' + el + '\'')
211 Deserializer.prototype.endNil =
function(data) {
216 Deserializer.prototype.endBoolean = function(data) {
220 else if (data ===
'0') {
224 throw new Error(
'Illegal boolean value \'' + data +
'\'')
229 Deserializer.prototype.endInt =
function(data) {
230 var value = parseInt(data, 10)
232 throw new Error(
'Expected an integer but got \'' + data +
'\'')
240 Deserializer.prototype.endDouble = function(data) {
241 var value = parseFloat(data)
243 throw new Error(
'Expected a double but got \'' + data +
'\'')
251 Deserializer.prototype.endString = function(data) {
256 Deserializer.prototype.endArray = function(data) {
257 var mark = this.marks.pop()
258 this.stack.splice(mark, this.stack.length - mark,
this.stack.slice(mark))
262 Deserializer.prototype.endStruct =
function(data) {
263 var mark = this.marks.pop()
265 , items = this.stack.slice(mark)
268 for (; i < items.length; i += 2) {
269 struct[items[i]] = items[i + 1]
271 this.stack.splice(mark, this.stack.length - mark,
struct)
275 Deserializer.prototype.endBase64 =
function(data) {
276 var buffer =
new Buffer(data,
'base64')
281 Deserializer.prototype.endDateTime = function(data) {
282 var date = dateFormatter.decodeIso8601(data)
287 var isInteger = /^-?\d+$/
288 Deserializer.prototype.endI8 = function(data) {
289 if (!isInteger.test(data)) {
290 throw new Error(
'Expected integer (I8) value but got \'' + data +
'\'')
297 Deserializer.prototype.endValue =
function(data) {
303 Deserializer.prototype.endParams =
function(data) {
304 this.responseType =
'params'
307 Deserializer.prototype.endFault =
function(data) {
308 this.responseType =
'fault'
311 Deserializer.prototype.endMethodResponse =
function(data) {
312 this.type =
'methodresponse'
315 Deserializer.prototype.endMethodName =
function(data) {
316 this.methodname = data
319 Deserializer.prototype.endMethodCall =
function(data) {
320 this.type =
'methodcall'
323 module.exports = Deserializer