Welcome to the "trac"-ing site of soap4r!
[soap4r] [httpclient] [openpgp4u] [pkcs1] [logger] [csv] [vtr]

Ticket #447 (assigned defect)

Opened 1 year ago

Last modified 1 year ago

LiteralRegistry#add_elesoap2stubobj tries to call two-result version of SOAPArray#each

Reported by: user Assigned to: nahi (accepted)
Priority: high Milestone: 1.5.9
Component: soap4r Version: 1.5
Keywords: Cc:

Description

soap4r 1.5.8 fails when mapping SOAP array elements to Ruby classes generated by wsdl2ruby.

The stack trace shows:

NoMethodError: undefined method `elename' for nil:NilClass
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/literalregistry.rb:278:in `add_elesoap2stubobj'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/baseData.rb:928:in `each'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/baseData.rb:927:in `each'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/literalregistry.rb:277:in `add_elesoap2stubobj'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/literalregistry.rb:250:in `elesoap2stubobj'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/literalregistry.rb:232:in `any2obj'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/literalregistry.rb:305:in `elesoapchild2obj'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/literalregistry.rb:280:in `add_elesoap2stubobj'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/baseData.rb:618:in `each'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/literalregistry.rb:277:in `add_elesoap2stubobj'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/literalregistry.rb:250:in `elesoap2stubobj'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/literalregistry.rb:232:in `any2obj'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/literalregistry.rb:53:in `soap2obj'
        from /usr/local/lib/ruby/gems/1.8/gems/soap4r-1.5.8/lib/soap/mapping/mapping.rb:150:in `_soap2obj'
[...]

The relevant lines of code in literalregistry.rb:278 are

  def add_elesoap2stubobj(node, obj, definition)
    vars = {}
    node.each do |name, value|
      item = definition.elements.find_element(value.elename)

The problem is that SOAPArray#each only yields one value on each iteration, not two:

# File soap/baseData.rb, line 652
  def each
    @data.each do |data|
      yield(data)
    end
  end

The error occurs while parsing the first Property element near the beginning of the response:

<SearchResult>
  <Properties>
    <Property Name="pageNumber">
      <Value>1</Value>
    </Property>
    <Property Name="totalReturned">
      <Value>10</Value>
    </Property>
    <Property Name="totalAvailable">
      <Value>121</Value>
    </Property>
    <Property Name="transactionId">
      <Value>test</Value>
    </Property>
    <Property Name="term">
      <Value>coffee</Value>
    </Property>
    <Property Name="zip">
      <Value>91203</Value>
    </Property>
    <Property Name="pageSize">
      <Value>10</Value>
    </Property>
  </Properties>
[...]

When the error occurs,

  • node is a SOAPArray containing the Properties element
  • obj is an empty array with the Ruby type ArrayOf_Properties
  • name is a a SOAPStruct, which contains the first Property element (with Name="pageNumber")
  • value is nil

The relevant fragment of the WSDL schema is

<xsd:complexType name="SearchResult">
        <xsd:sequence>
                <xsd:element name="Properties" nillable="true"
                        type="tns2:ArrayOf_Properties">
                </xsd:element>
                <xsd:element name="Categories" nillable="true"
                        type="tns2:ArrayOf_Categories">
                </xsd:element>
                <xsd:element name="Listings" nillable="true"
                        type="tns2:ArrayOf_Listings">
                </xsd:element>
        </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Property">
        <xsd:sequence>
                <xsd:element name="Value" type="xsd:string"
                        nillable="false" maxOccurs="1" minOccurs="0">							
                </xsd:element>
                <xsd:element name="Values" type="tns2:ArrayOf_Values" 
                        nillable="false" maxOccurs="1" minOccurs="0">
                </xsd:element>
                <xsd:element name="Record" type="tns2:ArrayOf_Properties"
                        nillable="false" maxOccurs="1" minOccurs="0">							
                </xsd:element>
                <xsd:element name="Records" type="tns2:ArrayOf_Records"
                        nillable="false" maxOccurs="1" minOccurs="0" >						
                </xsd:element>
        </xsd:sequence>
        
        <xsd:attribute name="Name" type="xsd:string">
        </xsd:attribute>
</xsd:complexType>

<xsd:complexType name="ArrayOf_Properties">
        <xsd:sequence>
                <xsd:element name="Property" type="tns2:Property"
                        nillable="true" maxOccurs="unbounded" />
        </xsd:sequence>
</xsd:complexType>

Attachments

IntegrationAPI2.wsdl (181.8 kB) - added by user on 10/26/07 05:36:04.
This is the WSDL I am using with wsdl2ruby.
distanceSearchOfTermAtZip_soap.txt (24.6 kB) - added by user on 10/26/07 05:55:17.
SOAP transcript for distanceSearchOfTermAtZip() call. Captured with Axis tcpmon, which has split the response into chunks.
getListing_soap.txt (4.7 kB) - added by user on 10/26/07 05:57:00.
SOAP transcript of getListing(). Also captured with Axis tcpmon.
integration_api2_irb.txt (6.8 kB) - added by user on 10/26/07 05:58:12.
Ruby irb transcript of calling the IntegrationAPI2 for the previously-posted SOAP calls.

Change History

10/18/07 11:07:01 changed by user

I was able hack a workaround. It works for the SOAP calls and WSDL that I posted in the bug report.

require 'rubygems'
gem 'soap4r', '= 1.5.8'

module SOAP
module Mapping
class LiteralRegistry

  def add_elesoap2stubobj(node, obj, definition)
    vars = {}
    node.each do |name, value|
      # Begin 1.5.8 hack
      # If node is a SOAPArray, #each only returns a single value per element.
      if value == nil
        name, value = 'item', name
        elename = value.type # elename is always '{}item' in a SOAPArray
      else
        elename = value.elename
      end
      item = definition.elements.find_element(elename)
      # End 1.5.8 hack
      if item
        child = elesoapchild2obj(value, item)
      else
        # unknown element is treated as anyType.
        child = any2obj(value)
      end
      if item and item.as_array?
        (vars[name] ||= []) << child
      elsif vars.key?(name)
        vars[name] = [vars[name], child].flatten
      else
        vars[name] = child
      end
    end
    if obj.is_a?(::Array) and is_stubobj_elements_for_array(vars)
      Array.instance_method(:replace).bind(obj).call(vars.values[0])
    else
      Mapping.set_attributes(obj, vars)
    end
  end

end # LiteralRegistry
end # Mapping
end # SOAP

10/18/07 23:38:37 changed by nahi

  • priority changed from normal to high.
  • status changed from new to assigned.
  • milestone changed from undefined to 1.5.9.

10/19/07 20:54:44 changed by user

Hi,

Sorry for late reply.

When soap4r is invoking literal service (LiteralRegistry? is used for literal service), SOAPArray should not be passed to it. There should be another bug around it. Can you create a concrete example of WSDL + client that causes the error above ?

10/26/07 05:36:04 changed by user

  • attachment IntegrationAPI2.wsdl added.

This is the WSDL I am using with wsdl2ruby.

10/26/07 05:55:17 changed by user

  • attachment distanceSearchOfTermAtZip_soap.txt added.

SOAP transcript for distanceSearchOfTermAtZip() call. Captured with Axis tcpmon, which has split the response into chunks.

10/26/07 05:57:00 changed by user

  • attachment getListing_soap.txt added.

SOAP transcript of getListing(). Also captured with Axis tcpmon.

10/26/07 05:58:12 changed by user

  • attachment integration_api2_irb.txt added.

Ruby irb transcript of calling the IntegrationAPI2 for the previously-posted SOAP calls.

11/06/07 18:05:21 changed by nahi

Oops, sorry for late reply. I was not aware the attachments. I'll check it. Please wait for a while.

Regards, // NaHi