DistelliServiceMarshallers 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/distelli/servicemarshallers.rb +431 -0
  2. metadata +77 -0
@@ -0,0 +1,431 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ require 'date'
5
+ require 'nokogiri'
6
+ require 'distelli/serviceinterface'
7
+
8
+ module Distelli
9
+ DATA_TYPES = ["int", "string", "boolean", "long", "double"]
10
+ class Marshaller
11
+ def initialize()
12
+ @object_map = Hash.new
13
+ end
14
+
15
+ def add_object(obj)
16
+ @object_map[obj.name] = obj
17
+ end
18
+ end
19
+
20
+ class MarshallException < StandardError
21
+ end
22
+
23
+ ################################
24
+ # JSON Marshaller
25
+ ################################
26
+
27
+ class JsonMarshaller < Marshaller
28
+ def initialize()
29
+ super
30
+ end
31
+
32
+ def encode(obj)
33
+ data = Hash.new
34
+ if obj.is_a?(Integer) or obj.is_a?(Float) or obj.is_a?(String) or obj.is_a?(TrueClass) or obj.is_a?(FalseClass)
35
+ return obj
36
+ elsif obj.is_a?(DateTime)
37
+ return obj.strftime("%Y-%m-%dT%H:%M:%S%z")
38
+ end
39
+
40
+ type_map_name = '_'+obj.class.name.split('::').last+'__type_map'
41
+ obj.instance_variables.each do |var|
42
+ var_name = var.to_s.delete('@')
43
+ if var_name == type_map_name
44
+ next
45
+ end
46
+ val = obj.instance_variable_get(var)
47
+ if val.is_a?(Integer) or val.is_a?(Float) or val.is_a?(String) or val.is_a?(TrueClass) or val.is_a?(FalseClass)
48
+ data[var_name] = val
49
+ elsif val.is_a?(DateTime)
50
+ data[var_name] = val.strftime("%Y-%m-%dT%H:%M:%S%z")
51
+ elsif val.instance_of?(Array)
52
+ array = Array.new
53
+ val.each do |elem|
54
+ encoded = encode(elem)
55
+ array.push(encoded)
56
+ end
57
+ data[var_name] = array
58
+ else val.kind_of?(Object)
59
+ encoded = encode(val)
60
+ data[var_name] = encoded
61
+ end
62
+ end
63
+ return data
64
+ end
65
+
66
+ def marshall(request)
67
+ data = encode(request)
68
+ request_wrapper = Hash.new
69
+ request_wrapper[request.class.name.split('::').last] = data
70
+ return request_wrapper.to_json
71
+ end
72
+
73
+ def marshall_error(error)
74
+ if not error.is_a?(Distelli::BaseException)
75
+ raise StandardError.new("Cannot marshall error: "+error.inspect)
76
+ end
77
+
78
+ err_msg = error.err_msg
79
+ err_code = error.err_code
80
+ return '{"Error":{"code":"'+err_code.to_s+'", "message":"'+err_msg.to_s+'"}}'
81
+ end
82
+
83
+ def unmarshall_error(json_data)
84
+ data_hash = JSON.load(json_data)
85
+ err_obj = data_hash[ServiceConstants::ERROR_KEY]
86
+ if err_obj == nil
87
+ return nil
88
+ end
89
+ err_code = err_obj[ServiceConstants::ERR_CODE_KEY]
90
+ err_msg = err_obj[ServiceConstants::ERR_MSG_KEY]
91
+ return [err_code, err_msg]
92
+ end
93
+
94
+ def unmarshall(json_data)
95
+ data_hash = JSON.load(json_data)
96
+ data_hash.each_pair do |k,v|
97
+ if v.is_a?(Hash)
98
+ return unmarshall_obj(k, v)
99
+ else
100
+ raise MarshallException.new("Invalid data "+json_data)
101
+ end
102
+ end
103
+ end
104
+
105
+ private
106
+ def to_obj_list(obj_name, list_data)
107
+ obj_list = Array.new
108
+ # If its a list type then its a list of complex objects
109
+ if list_data.is_a?(Array)
110
+ list_data.each do |v|
111
+ obj = unmarshall_obj(obj_name, v)
112
+ obj_list.push(obj)
113
+ end
114
+ else # Else its a map (hopefully) and its only a single complex object
115
+ obj = unmarshall_obj(obj_name, list_data)
116
+ obj_list.push(obj)
117
+ end
118
+ return obj_list
119
+ end
120
+
121
+ private
122
+ def to_date_list(list_data)
123
+ date_list = Array.new
124
+ list_data.each do |v|
125
+ date_val = DateTime.strptime(v, "%Y-%m-%dT%H:%M:%S%z")
126
+ date_list.push(date_val)
127
+ end
128
+ return date_list
129
+ end
130
+
131
+ private
132
+ def unmarshall_list(list_data, data_type)
133
+ if list_data.length == 0
134
+ return Array.new
135
+ end
136
+ actual_list = list_data
137
+ if data_type == "date"
138
+ return to_date_list(actual_list)
139
+ elsif DATA_TYPES.include?(data_type)
140
+ return actual_list
141
+ else
142
+ return to_obj_list(data_type, actual_list)
143
+ end
144
+ end
145
+
146
+ private
147
+ def unmarshall_obj(obj_name, data)
148
+ # puts "Unmarshalling object: "+obj_name
149
+ if !@object_map.has_key?(obj_name)
150
+ raise MarshallException.new("Unknown object: "+obj_name.to_s)
151
+ end
152
+ class_obj = @object_map[obj_name]
153
+ instance = class_obj.new
154
+ type_map_name = '@_'+obj_name+'__type_map'
155
+ type_map = instance.instance_variable_get(type_map_name)
156
+ data.each_pair do |k,v|
157
+ if !type_map.include?(k)
158
+ raise MarshallException.new("Unknown type for field: "+k)
159
+ end
160
+ field_type = type_map[k]
161
+ if field_type.is_a?(Array)
162
+ field_type = field_type[0]
163
+ end
164
+ if v.is_a?(Hash)
165
+ um_obj = unmarshall_obj(field_type, v)
166
+ instance.instance_variable_set('@'+k, um_obj)
167
+ elsif v.is_a?(Array)
168
+ um_list = unmarshall_list(v, field_type)
169
+ instance.instance_variable_set('@'+k, um_list)
170
+ else
171
+ if field_type == "date"
172
+ date_val = DateTime.strptime(v, "%Y-%m-%dT%H:%M:%S%z")
173
+ instance.instance_variable_set('@'+k, date_val)
174
+ elsif field_type == "int"
175
+ instance.instance_variable_set('@'+k, v.to_i)
176
+ elsif field_type == "double"
177
+ instance.instance_variable_set('@'+k, v.to_f)
178
+ elsif field_type == "long"
179
+ instance.instance_variable_set('@'+k, v.to_i)
180
+ else
181
+ instance.instance_variable_set('@'+k, v)
182
+ end
183
+ end
184
+ end
185
+ return instance
186
+ end
187
+ end
188
+
189
+ ################################
190
+ # XML Marshaller
191
+ ################################
192
+
193
+ class XmlMarshaller < Marshaller
194
+ def initialize()
195
+ super
196
+ end
197
+
198
+ def marshall(request)
199
+ root_tag_name = request.class.name.split('::').last
200
+ return ['<', root_tag_name,'>', encode_obj_xml(request), '</', root_tag_name, '>'].join('')
201
+ end
202
+
203
+ def unmarshall(xml_data)
204
+ xml_doc = Nokogiri::XML(xml_data)
205
+ root_node = xml_doc.root
206
+ return unmarshall_obj(root_node)
207
+ end
208
+
209
+ def marshall_error(error)
210
+ if not error.is_a?(Distelli::BaseException)
211
+ raise StandardError.new("Cannot marshall error: "+error.inspect)
212
+ end
213
+
214
+ err_msg = error.err_msg
215
+ err_code = error.err_code
216
+ return "<Error><code>"+err_code.to_s+"</code><message>"+err_msg.to_s+"</message></Error>"
217
+ end
218
+
219
+ ##########################################################
220
+ # Unmarshalls an xml error response and returns the error
221
+ # code and message.
222
+ #
223
+ # This is what an xml error looks like:
224
+ #
225
+ # <Error>
226
+ # <code>MalformedRequest</code>
227
+ # <message>The request is malformed</message>
228
+ # </Error>
229
+ ##########################################################
230
+ def unmarshall_error(xml_data)
231
+ xml_doc = Nokogiri::XML(xml_data)
232
+ err_code_elem = xml_doc.xpath('//'+ServiceConstants::ERROR_KEY+'//'+ServiceConstants::ERR_CODE_KEY)
233
+ err_msg_elem = xml_doc.xpath('//'+ServiceConstants::ERROR_KEY+'//'+ServiceConstants::ERR_MSG_KEY)
234
+ err_code = nil
235
+ err_msg = nil
236
+ if err_code_elem != nil
237
+ err_code = err_code_elem.text
238
+ end
239
+ if err_msg_elem != nil
240
+ err_msg = err_msg_elem.text
241
+ end
242
+ return [err_code, err_msg]
243
+ end
244
+
245
+ private
246
+ def get_node_text(node)
247
+ children = node.children
248
+ if children.length == 0
249
+ return nil
250
+ end
251
+ if children.length != 1
252
+ raise MarshallException.new(node.to_s+" is not a text node")
253
+ end
254
+
255
+ text_node = children[0]
256
+ return text_node.content
257
+ end
258
+
259
+ private
260
+ def unmarshall_list(obj_node, list_type)
261
+ # puts "Unmarshalling list at "+obj_node.to_s
262
+ list_elements = obj_node.children
263
+ list_data = Array.new
264
+ list_elements.each do |elem|
265
+ # If its a simple type list
266
+ # puts "Unmarshalling list elemnt "+elem.name+" "+list_type
267
+ if list_type == "date"
268
+ obj_node_content = get_node_text(elem)
269
+ date_val = DateTime.strptime(obj_node.content, "%Y-%m-%dT%H:%M:%S%z")
270
+ list_data.push(date_val)
271
+ elsif DATA_TYPES.include?(list_type)
272
+ # puts "Unmarshalling primitive list element "+elem.name+" "+list_type
273
+ elem_text = get_node_text(elem)
274
+ if list_type == "int"
275
+ list_data.push(elem_text.to_i)
276
+ elsif list_type == "long"
277
+ list_data.push(elem_text.to_i)
278
+ elsif list_type == "boolean"
279
+ if elem_text.downcase == "true"
280
+ list_data.push(true)
281
+ else
282
+ list_data.push(false)
283
+ end
284
+ elsif list_type == "double"
285
+ list_data.push(elem_text.to_f)
286
+ else
287
+ list_data.push(elem_text)
288
+ end
289
+ else
290
+ # puts "Unmarshalling complex list element"+elem.name+" "+list_type
291
+ # Else its a list of complex types
292
+ unmarshalled_obj = unmarshall_obj(elem)
293
+ list_data.push(unmarshalled_obj)
294
+ end
295
+ end
296
+ return list_data
297
+ end
298
+
299
+ private
300
+ def unmarshall_obj(obj_node, parent_obj=nil)
301
+ # puts "Unmarshalling "+obj_node.name+" "+obj_node.inspect+" member of "+parent_obj.to_s
302
+ obj_name = obj_node.name
303
+ obj_instance = nil
304
+ if parent_obj == nil
305
+ if !@object_map.has_key?(obj_name)
306
+ raise MarshallException.new("Unknown object: "+obj_name.to_s)
307
+ end
308
+ class_obj = @object_map[obj_name]
309
+ obj_instance = class_obj.new
310
+ else
311
+ # Parent object is not none. So obj_node is a member of
312
+ # obj_instance and we can get the type from the parent obj
313
+ # First get the type map
314
+ # puts "Processing "+obj_name
315
+ type_map_name = '@_'+parent_obj.class.name.split('::').last+'__type_map'
316
+ type_map = parent_obj.instance_variable_get(type_map_name)
317
+ if type_map == nil
318
+ raise MarshallException.new(parent_obj.class.name+" cannot be unmarshalled. Missing type_map: "+type_map_name)
319
+ end
320
+
321
+ if !type_map.include?(obj_name)
322
+ raise MarshallException.new("Unknown type for field: "+obj_name+" in obj "+obj_instance.to_s)
323
+ end
324
+ obj_type = type_map[obj_name]
325
+ # puts "Unmarshalling "+obj_name+" "+obj_type.to_s
326
+ # Obj type can be either a list or one of the defined
327
+ # primitive types or a complex type
328
+ if obj_type.is_a?(Array)
329
+ list_type = obj_type[0]
330
+ # puts obj_name+" is a list in "+parent_obj.to_s+" of type "+list_type
331
+ unmarshalled_list = unmarshall_list(obj_node, list_type)
332
+ # puts "Unmarshalled list"+unmarshalled_list.to_s
333
+ parent_obj.instance_variable_set('@'+obj_name, unmarshalled_list)
334
+ return obj_instance
335
+ elsif obj_type == "date"
336
+ date_val = DateTime.strptime(obj_node.content, "%Y-%m-%dT%H:%M:%S%z")
337
+ parent_obj.instance_variable_set('@'+obj_name, date_val)
338
+ return obj_instance
339
+ elsif DATA_TYPES.include?(obj_type)
340
+ # puts obj_name+" is a primitive "+obj_type+" "+obj_node+" in "+parent_obj.to_s
341
+ if obj_type == "int" or obj_type == "long"
342
+ parent_obj.instance_variable_set('@'+obj_name, get_node_text(obj_node).to_i)
343
+ elsif obj_type == "boolean"
344
+ obj_text = get_node_text(obj_node)
345
+ if obj_text.downcase == "true"
346
+ parent_obj.instance_variable_set('@'+obj_name, true)
347
+ else
348
+ parent_obj.instance_variable_set('@'+obj_name, false)
349
+ end
350
+ elsif obj_type == "double"
351
+ parent_obj.instance_variable_set('@'+obj_name, get_node_text(obj_node).to_f)
352
+ else
353
+ parent_obj.instance_variable_set('@'+obj_name, get_node_text(obj_node))
354
+ end
355
+ return obj_instance
356
+ else
357
+ # puts obj_name+" is a complex type "+obj_type+" in "+parent_obj.to_s
358
+ if !@object_map.has_key?(obj_type)
359
+ raise MarshallException.new("Unknown object: "+obj_type.to_s)
360
+ end
361
+
362
+ class_obj = @object_map[obj_type]
363
+ obj_instance = class_obj.new
364
+ parent_obj.instance_variable_set('@'+obj_name, obj_instance)
365
+ end
366
+ end
367
+ # puts "Processing children of "+obj_node.name
368
+ children = obj_node.children
369
+ children.each do |child|
370
+ unmarshall_obj(child, parent_obj=obj_instance)
371
+ end
372
+ return obj_instance
373
+ end
374
+
375
+ private
376
+ def encode_primitive_xml(tag_name, value)
377
+ if value.is_a?(DateTime)
378
+ return ['<', tag_name, '>', value.strftime("%Y-%m-%dT%H:%M:%S%z"), '</', tag_name, '>'].join('')
379
+ else
380
+ ['<', tag_name, '>', value.to_s, '</', tag_name, '>'].join('')
381
+ end
382
+ end
383
+
384
+ private
385
+ def list_to_xml(list_obj, list_type)
386
+ xml_data = Array.new
387
+ list_obj.each do |item|
388
+ if item.is_a?(DateTime) or item.is_a?(Integer) or item.is_a?(Float) or item.is_a?(String) or item.is_a?(TrueClass) or item.is_a?(FalseClass)
389
+ xml_data.push(encode_primitive_xml(list_type, item))
390
+ else
391
+ xml_data.push(['<', list_type, '>', encode_obj_xml(item), '</', list_type, '>'].join(''))
392
+ end
393
+ end
394
+ return xml_data.join('')
395
+ end
396
+
397
+ private
398
+ def encode_obj_xml(obj)
399
+ type_map_name = '_'+obj.class.name.split('::').last+'__type_map'
400
+ type_map = obj.instance_variable_get('@'+type_map_name)
401
+ xml_data = Array.new
402
+
403
+ obj.instance_variables.each do |var|
404
+ k = var.to_s.delete('@')
405
+ # puts "Var: "+var.to_s+" TMN: "+type_map_name.to_s
406
+ if k == type_map_name
407
+ next
408
+ end
409
+ v = obj.instance_variable_get(var)
410
+ data_type = type_map[k]
411
+
412
+ if data_type.is_a?(Array)
413
+ data_type = data_type[0]
414
+ end
415
+
416
+ # print "Encoding "+k.to_s+" "+v.to_s
417
+ key = k
418
+ if v.is_a?(Array)
419
+ xml_data.push(['<', key, '>', list_to_xml(v, data_type), '</', key, '>'].join(''))
420
+ elsif v.is_a?(DateTime)
421
+ xml_data.push(encode_primitive_xml(key, v))
422
+ elsif DATA_TYPES.include?(data_type)
423
+ xml_data.push(encode_primitive_xml(key, v))
424
+ else
425
+ xml_data.push(['<', key, '>', encode_obj_xml(v), '</', key, '>'].join(''))
426
+ end
427
+ end
428
+ return xml_data.join('')
429
+ end
430
+ end
431
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: DistelliServiceMarshallers
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rahul Singh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: DistelliServiceInterface
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: nokogiri
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Distelli Service Marshallers for Ruby Servers and Clients
47
+ email: rsingh@distelli.com
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - lib/distelli/servicemarshallers.rb
53
+ homepage: http://www.distelli.com/
54
+ licenses: []
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 1.8.23
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Distelli Service Marshaller classes
77
+ test_files: []