File: deserialization.rb

package info (click to toggle)
ruby-rbvmomi 1.8.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,756 kB
  • sloc: ruby: 5,590; sh: 36; makefile: 7
file content (248 lines) | stat: -rw-r--r-- 6,211 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# Copyright (c) 2011 VMware, Inc.  All Rights Reserved.
require 'time'

module RbVmomi

class NewDeserializer
  NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'

  DEMANGLED_ARRAY_TYPES = {
    'AnyType' => 'xsd:anyType',
    'DateTime' => 'xsd:dateTime',
  }
  %w(Boolean String Byte Short Int Long Float Double).each do |x|
    DEMANGLED_ARRAY_TYPES[x] = "xsd:#{x.downcase}"
  end

  BUILTIN_TYPE_ACTIONS = {
    'xsd:string' => :string,
    'xsd:boolean' => :boolean,
    'xsd:byte' => :int,
    'xsd:short' => :int,
    'xsd:int' => :int,
    'xsd:long' => :int,
    'xsd:float' => :float,
    'xsd:dateTime' => :date,
    'PropertyPath' => :string,
    'MethodName' => :string,
    'TypeName' => :string,
    'xsd:base64Binary' => :binary,
    'KeyValue' => :keyvalue,
  }

  BUILTIN_TYPE_ACTIONS.dup.each do |k,v|
    if k =~ /^xsd:/
      BUILTIN_TYPE_ACTIONS[$'] = v
    end
  end

  def initialize conn
    @conn = conn
    @loader = conn.class.loader
  end

  def deserialize node, type=nil
    type_attr = node['type']

    # Work around for 1.5.x which doesn't populate node['type']
    # XXX what changed
    if node.attributes['type'] and not type_attr
      type_attr = node.attributes['type'].value
    end

    type = type_attr if type_attr

    if action = BUILTIN_TYPE_ACTIONS[type]
      case action
      when :string
        node.content
      when :boolean
        node.content == '1' || node.content == 'true'
      when :int
        node.content.to_i
      when :float
        node.content.to_f
      when :date
        leaf_date node
      when :binary
        leaf_binary node
      when :keyvalue
        leaf_keyvalue node
      else fail
      end
    else
      if type =~ /:/
        type = type.split(":", 2)[1]
      end
      if type =~ /^ArrayOf/
        type = DEMANGLED_ARRAY_TYPES[$'] || $'
        return node.children.select(&:element?).map { |c| deserialize c, type }
      end
      if type =~ /:/
        type = type.split(":", 2)[1]
      end

      klass = @loader.get(type) or fail "no such type '#{type}'"
      case klass.kind
      when :data
        traverse_data node, klass
      when :enum
        node.content
      when :managed
        leaf_managed node, klass
      else fail
      end
    end
  end

  def traverse_data node, klass
    obj = klass.new nil
    props = obj.props
    children = node.children.select(&:element?)
    n = children.size
    i = 0

    klass.full_props_desc.each do |desc|
      name = desc['name']
      child_type = desc['wsdl_type']

      # Ignore unknown fields
      while child = children[i] and not klass.full_props_set.member? child.name
        i += 1
      end

      if desc['is-array']
        a = []
        while ((child = children[i]) && (child.name == name))
          child = children[i]
          a << deserialize(child, child_type)
          i += 1
        end
        props[name.to_sym] = a
      elsif ((child = children[i]) && (child.name == name))
        props[name.to_sym] = deserialize(child, child_type)
        i += 1
      end
    end

    obj
  end

  def leaf_managed node, klass
    type_attr = node['type']
    klass = @loader.get(type_attr) if type_attr
    klass.new(@conn, node.content)
  end

  def leaf_date node
    Time.parse node.content
  end

  def leaf_binary node
    node.content.unpack('m')[0]
  end

  # XXX does the value need to be deserialized?
  def leaf_keyvalue node
    h = {}
    node.children.each do |child|
      next unless child.element?
      h[child.name] = child.content
    end
    [h['key'], h['value']]
  end 
end

class OldDeserializer
  NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'

  def initialize conn
    @conn = conn
  end

  def deserialize xml, typename=nil
    if IS_JRUBY
      type_attr = xml.attribute_nodes.find { |a| a.name == 'type' &&
                                                 a.namespace &&
                                                 a.namespace.prefix == 'xsi' }
    else
      type_attr = xml.attribute_with_ns('type', NS_XSI)
    end
    typename = (type_attr || typename).to_s

    if typename =~ /^ArrayOf/
      typename = demangle_array_type $'
      return xml.children.select(&:element?).map { |c| deserialize c, typename }
    end

    t = @conn.type typename
    if t <= BasicTypes::DataObject
      props_desc = t.full_props_desc
      h = {}
      props_desc.select { |d| d['is-array'] }.each { |d| h[d['name'].to_sym] = [] }
      xml.children.each do |c|
        next unless c.element?
        field = c.name.to_sym
        d = t.find_prop_desc(field.to_s) or next
        o = deserialize c, d['wsdl_type']
        if h[field].is_a? Array
          h[field] << o
        else
          h[field] = o
        end
      end
      t.new h
    elsif t == BasicTypes::ManagedObjectReference
      @conn.type(xml['type']).new @conn, xml.text
    elsif t <= BasicTypes::ManagedObject
      @conn.type(xml['type'] || t.wsdl_name).new @conn, xml.text
    elsif t <= BasicTypes::Enum
      xml.text
    elsif t <= BasicTypes::KeyValue
      h = {}
      xml.children.each do |c|
        next unless c.element?
        h[c.name] = c.text
      end
      [h['key'], h['value']]
    elsif t <= String
      xml.text
    elsif t <= Symbol
      xml.text.to_sym
    elsif t <= Integer
      xml.text.to_i
    elsif t <= Float
      xml.text.to_f
    elsif t <= Time
      Time.parse xml.text
    elsif t == BasicTypes::Boolean
      xml.text == 'true' || xml.text == '1'
    elsif t == BasicTypes::Binary
      xml.text.unpack('m')[0]
    elsif t == BasicTypes::AnyType
      fail "attempted to deserialize an AnyType"
    else fail "unexpected type #{t.inspect} (#{t.ancestors * '/'})"
    end
  rescue
    $stderr.puts "#{$!.class} while deserializing #{xml.name} (#{typename}):"
    $stderr.puts xml.to_s
    raise
  end

  def demangle_array_type x
    case x
    when 'AnyType' then 'anyType'
    when 'DateTime' then 'dateTime'
    when 'Boolean', 'String', 'Byte', 'Short', 'Int', 'Long', 'Float', 'Double' then x.downcase
    else x
    end
  end
end

if ENV['RBVMOMI_NEW_DESERIALIZER'] == '1' || true # Always use new one now
  Deserializer = NewDeserializer
else
  Deserializer = OldDeserializer
end

end