def obj2xml xml, name, type, is_array, o, attrs={}
    expected = type(type)
    fail "expected array for '#{name}', got #{o.class.wsdl_name}" if is_array and not (o.is_a? Array or (o.is_a? Hash and expected == BasicTypes::KeyValue))
    case o
    when Array, BasicTypes::KeyValue
      if o.is_a? BasicTypes::KeyValue and expected != BasicTypes::KeyValue
        fail "expected #{expected.wsdl_name} for '#{name}', got KeyValue"
      elsif expected == BasicTypes::KeyValue and not is_array
        xml.tag! name, attrs do
          xml.tag! 'key', o[0].to_s
          xml.tag! 'value', o[1].to_s
        end
      else
        fail "expected #{expected.wsdl_name} for '#{name}', got array" unless is_array
        o.each do |e|
          obj2xml xml, name, expected.wsdl_name, false, e, attrs
        end
      end
    when BasicTypes::ManagedObject
      fail "expected #{expected.wsdl_name} for '#{name}', got #{o.class.wsdl_name} for field #{name.inspect}" if expected and not expected >= o.class and not expected == BasicTypes::AnyType
      xml.tag! name, o._ref, :type => o.class.wsdl_name
    when BasicTypes::DataObject
      if expected and not expected >= o.class and not expected == BasicTypes::AnyType
        fail "expected #{expected.wsdl_name} for '#{name}', got #{o.class.wsdl_name} for field #{name.inspect}"
      end
      xml.tag! name, attrs.merge("xsi:type" => o.class.wsdl_name) do
        o.class.full_props_desc.each do |desc|
          if o.props.member? desc['name'].to_sym
            v = o.props[desc['name'].to_sym]
            next if v.nil?
            obj2xml xml, desc['name'], desc['wsdl_type'], desc['is-array'], v
          end
        end
      end
    when BasicTypes::Enum
      xml.tag! name, o.value.to_s, attrs
    when Hash
      if expected == BasicTypes::KeyValue and is_array
        obj2xml xml, name, type, is_array, o.to_a, attrs
      else
        fail "expected #{expected.wsdl_name} for '#{name}', got a hash" unless expected <= BasicTypes::DataObject
        obj2xml xml, name, type, false, expected.new(o), attrs
      end
    when true, false
      fail "expected #{expected.wsdl_name} for '#{name}', got a boolean" unless [BasicTypes::Boolean, BasicTypes::AnyType].member? expected
      attrs['xsi:type'] = 'xsd:boolean' if expected == BasicTypes::AnyType
      xml.tag! name, (o ? '1' : '0'), attrs
    when Symbol, String
      if expected == BasicTypes::Binary
        attrs['xsi:type'] = 'xsd:base64Binary' if expected == BasicTypes::AnyType
        xml.tag! name, [o].pack('m').chomp.gsub("\n", ""), attrs
      else
        attrs['xsi:type'] = 'xsd:string' if expected == BasicTypes::AnyType
        xml.tag! name, o.to_s, attrs
      end
    when Integer
      attrs['xsi:type'] = 'xsd:long' if expected == BasicTypes::AnyType
      xml.tag! name, o.to_s, attrs
    when Float
      attrs['xsi:type'] = 'xsd:double' if expected == BasicTypes::AnyType
      xml.tag! name, o.to_s, attrs
    when DateTime
      attrs['xsi:type'] = 'xsd:dateTime' if expected == BasicTypes::AnyType
      xml.tag! name, o.strftime('%FT%T%:z'), attrs
    when Time
      attrs['xsi:type'] = 'xsd:dateTime' if expected == BasicTypes::AnyType
      xml.tag! name, o.iso8601, attrs
    when BasicTypes::Int
      attrs['xsi:type'] = 'xsd:int'
      xml.tag! name, o.to_s, attrs
    else fail "unexpected object class #{o.class} for '#{name}'"
    end
    xml
  rescue
    $stderr.puts "#{$!.class} while serializing #{name} (#{type}):"
    PP.pp o, $stderr
    raise
  end
def obj2xml xml, name, type, is_array, o, attrs={}
    expected = type(type)
    fail "expected array for '#{name}', got #{o.class.wsdl_name}" if is_array and not (o.is_a? Array or (o.is_a? Hash and expected == BasicTypes::KeyValue))
    case o
    when Array, BasicTypes::KeyValue
      if o.is_a? BasicTypes::KeyValue and expected != BasicTypes::KeyValue
        fail "expected #{expected.wsdl_name} for '#{name}', got KeyValue"
      elsif expected == BasicTypes::KeyValue and not is_array
        xml.tag! name, attrs do
          xml.tag! 'key', o[0].to_s
          xml.tag! 'value', o[1].to_s
        end
      else
        fail "expected #{expected.wsdl_name} for '#{name}', got array" unless is_array
        o.each do |e|
          obj2xml xml, name, expected.wsdl_name, false, e, attrs
        end
      end
    when BasicTypes::ManagedObject
      fail "expected #{expected.wsdl_name} for '#{name}', got #{o.class.wsdl_name} for field #{name.inspect}" if expected and not expected >= o.class and not expected == BasicTypes::AnyType
      xml.tag! name, o._ref, :type => o.class.wsdl_name
    when BasicTypes::DataObject
      if expected and not expected >= o.class and not expected == BasicTypes::AnyType
        fail "expected #{expected.wsdl_name} for '#{name}', got #{o.class.wsdl_name} for field #{name.inspect}"
      end
      xml.tag! name, attrs.merge("xsi:type" => o.class.wsdl_name) do
        o.class.full_props_desc.each do |desc|
          if o.props.member? desc['name'].to_sym
            v = o.props[desc['name'].to_sym]
            next if v.nil?
            obj2xml xml, desc['name'], desc['wsdl_type'], desc['is-array'], v
          end
        end
      end
    when BasicTypes::Enum
      xml.tag! name, o.value.to_s, attrs
    when Hash
      if expected == BasicTypes::KeyValue and is_array
        obj2xml xml, name, type, is_array, o.to_a, attrs
      else
        fail "expected #{expected.wsdl_name} for '#{name}', got a hash" unless expected <= BasicTypes::DataObject
        obj2xml xml, name, type, false, expected.new(o), attrs
      end
    when true, false
      fail "expected #{expected.wsdl_name} for '#{name}', got a boolean" unless [BasicTypes::Boolean, BasicTypes::AnyType].member? expected
      attrs['xsi:type'] = 'xsd:boolean' if expected == BasicTypes::AnyType
      xml.tag! name, (o ? '1' : '0'), attrs
    when Symbol, String
      if expected == BasicTypes::Binary
        attrs['xsi:type'] = 'xsd:base64Binary' if expected == BasicTypes::AnyType
        xml.tag! name, [o].pack('m').chomp.gsub("\n", ""), attrs
      else
        attrs['xsi:type'] = 'xsd:string' if expected == BasicTypes::AnyType
        xml.tag! name, o.to_s, attrs
      end
    when Integer
      attrs['xsi:type'] = 'xsd:long' if expected == BasicTypes::AnyType
      xml.tag! name, o.to_s, attrs
    when Float
      attrs['xsi:type'] = 'xsd:double' if expected == BasicTypes::AnyType
      xml.tag! name, o.to_s, attrs
    when DateTime
      attrs['xsi:type'] = 'xsd:dateTime' if expected == BasicTypes::AnyType
      xml.tag! name, o.strftime('%FT%T%:z'), attrs
    when Time
      attrs['xsi:type'] = 'xsd:dateTime' if expected == BasicTypes::AnyType
      xml.tag! name, o.iso8601, attrs
    when BasicTypes::Int
      attrs['xsi:type'] = 'xsd:int'
      xml.tag! name, o.to_s, attrs
    else fail "unexpected object class #{o.class} for '#{name}'"
    end
    xml
  rescue
    $stderr.puts "#{$!.class} while serializing #{name} (#{type}):"
    PP.pp o, $stderr
    raise
  end
hic sunt dracones