Search code examples
perlsoapsoaplite

Define namespaces in the "method" element using SOAP::Lite


I am using SOAP::Lite to connect to an outside service. After much trial and error and communication with the other company, I have discovered the problem.

My code looks like this:

$soap = new SOAP::Lite
    ->service($wsdl_link)
    ->uri($url_link)
    ->proxy($proxy_link)
    ->on_action(sub { sprintf '"%s%s"', shift, shift });
my $resp = $soap->call('CreateAssignment',SOAP::Data->type('xml'=>$xml),
                                SOAP::Header->type('xml'=>$headXML));

This produces the following xml:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"   >
<soap:Header>
...
</soap:Header>
<soap:Body>
<CreateAssignment xmlns="url_link">
...
</CreateAssignment></soap:Body></soap:Envelope>

(where url_link is a valid url)

I need to define additional namespaces. I have done this by adding ->ns(namespace, prefix) to my code. However, this adds the additional namespaces to the "envelope" tag. I have been informed by the company that the namespaces need to be on the "CreateAssignment" tag. Indeed, when I make the appropriate change and run it using SOAP UI, it works beautifully.

I have tried adding the "CreateAssignment" tag to my xml and running the call() function without a method. SOAP::Lite wraps the xml in a generic tag.

I have read the SOAP::Lite documentation, I have asked search engines, I have asked colleagues and no one has an answer.

Is there a way to force SOAP::Lite to put the namespace declarations where I need them?

If not, what is a better module to use?


Solution

  • I ended up sitting down with another coworker and reading the source code of SOAP::Lite - we discovered that the method tag was built in sub envelope. There is an if-statement whereby the module will use the entire object if instead of a string object a SOAP::Data object is passed in as the method:

    elsif (UNIVERSAL::isa($method => 'SOAP::Data')) {
                $body = $method;
            }
    

    I changed from this:

    $soap = new SOAP::Lite
    ->service($wsdl_link)
    ->uri($url_link)
    ->proxy($proxy_link)
    ->on_action(sub { sprintf '"%s%s"', shift, shift });
    my $resp = $soap->call('CreateAssignment',SOAP::Data->type('xml'=>$xml),
                                    SOAP::Header->type('xml'=>$headXML));
    

    To this:

    $method = SOAP::Data->new(name=>'ns4:CreateAssignment');
    $method->attr({'xmlns'=> $namespaceOne,
                'xmlns:ns2'=> $namespaceTwo,
                'xmlns:ns3'=> $namespaceThree,
                'xmlns:ns4'=> $namespaceFour,
                'xmlns:ns5'=> $namespaceFive});
    
    $soap = new SOAP::Lite
            ->service($wsdl_link)
            ->uri($url_link)
            ->proxy($proxy_link)
            ->on_action(sub { sprintf '"%s%s"', shift, shift });
    
    my $resp = $soap->call($method,SOAP::Data->type('xml'=>$xml),
                                    SOAP::Header->type('xml'=>$headXML));
    

    This created my method tag exactly as the company required:

    <ns4:CreateAssignment xmlns="namespaceOne" xmlns:ns2="namespaceTwo" xmlns:ns3="namespaceThree" xmlns:ns4="namespaceFour" xmlns:ns5="namespaceFive">
    ...
    </ns4:CreateAssignment>