« How do you turnoff the blasted c-gensym elements? | Main | SOAP::Lite 0.60 Released! »

Comments

Thom Eden

What happens if you have an array nested within an array?

e.g.
person
address
addressTypeBusiness/addressType
addressLineList
addressLine100 Main St./addressLine
addressLineSuite 32/addressLine
/addressLineList
/address
address
addressTypeHome/addressType
addressLineList
addressLine100 Jones Dr./addressLine
addressLineApt 1-Y/addressLine
/addressLineList
/address
/person

Byrne

In this case you will want to use $som->dataof and first loop over the outermost element, and then loop over the inner elements using valueof. Make sense?

Thom Eden

Actually, no. My code looks like this:

sub add {
my $obj = shift; # get rid of class/object reference
my $som = pop; # pop SOAP Envelope (SOAP::Lite SOM object) from end of list

if ($som->match(SOAP::SOM::envelope)) {
my $body = $som->valueof("Body");
my $person = $body->{'person'};

my $b = $som->match('//person/address');
my $i = 1;
print scalar $b->dataof, "\n";
foreach my $address($b->dataof) {
print "\n\n$i addressType:\t\t", $address->value->{'addressType'}, "\n";
if (exists $address->value->{'addressLineList'}) {
if (exists $address->value->{'addressLineList'}->{'addressLine'}) {
# foreach my $addressLine($b->dataof('//addressLine')) {
# print "addressLine:\t\t", $addressLine->value, "\n";
# }
foreach my $addressLine($b->valueof('//addressLine')) {
print "addressLine:\t\t", $addressLine, "\n";
}
}
}
$i++;
}


} else {
print "BPCDecisionService did not receive a SOAP Envelope (SOM object)";
}

};

Here is my output:

1 addressType: Business
addressLine: 100 Main St.
addressLine: Suite 32
addressLine: 100 Jones Dr.
addressLine: Apt 1-Y


2 addressType: Home
addressLine: 100 Main St.
addressLine: Suite 32
addressLine: 100 Jones Dr.
addressLine: Apt 1-Y

As you can see, the addressLine element is getting picked up for both sets of addresses, clearly not the intended result.

Dan O'Donnell

Should've been able to extrapolate from "Parsing a result object from SOAP::Lite" but wasn't ... or perhaps did but struggled a lot. At any rate, a less succinct example for folks as my self, not yet adept at groking all the implications of SOAP::Lite documentation, may help folk like me from bothering folk like you (from yahoo 3067).

use SOAP::Lite;
my $xml = qq(0Success00RO000nal Rate Bed /Shower Only0.46 (Euro) Per Room Per Stay, not included in total0000000.46 (Euro) Per Room Per Stay, not included in total0truefalse);
my $som = SOAP::Deserializer->deserialize($xml);

# Iterate through all hash elements of product description array of hashes
# Use match() in boolean context to control loop
# Use match() in SOM context to access matched hash
for (my $i = 1; $som->match("//product-desc/[$i]"); $i++) {
print "\nproduct-desc match: $i:\n";
my $href = $som->valueof();
my @pdk = keys %$href;
foreach my $dk (@pdk){print $dk, ": ", $href->{$dk}, "\n";}
}

James

Is it possible to retrieve the RAW xml result data? Without using outputxml? I still want to use the SOM fault handleing, and I need to retreieve an XML string. I don't want to loop over the hashes when all I need is the XML string?

Example:
This is what I get back from the .NET server
(cleaned up)
I want to be able to easily retrieve everything
between fooResult and /fooResult with out having to parse through hashes.

Envelope
Body
fooResponse
fooResult
MyXmlBlob
manyelements
/manyelements
/MyXmlBlob
/fooResult
/fooResponse
/Body
/Envelope

Byrne Reese

James,

A good question that is asked a lot. The short answer is "no, there is not." That will hopefully be changed in future releases however. But let's talk a little bit about work arounds.

A collegue of mine tried to do this at work, but he ended up having to write his own SOAP client. You can use portions of SOAP::Lite to deserialize an XML document for example - that will produce the SOAP::SOM object you want, but there is no way that I know of to redirect the result XML to a buffer of some kind without modifying SOAP::Lite... but let's talk about *that*.

If you did want to modify SOAP::Lite, I might consider doing this:

Look for 'return $response if $self->outputxml;' in lib/SOAP/Lite.pm's call subroutine... at that point the variable $response contains the raw text, but it also will halt execution. What you might consider doing is this:

$self->{'_raw_xml'} = $response; # insert me!
return $response if $self->outputxml;

James

Thanks Byrne,
I modified SOAP::Lite and it works perfectly. But I did one thing differently.

Instead of
$self->{'_raw_xml'} = $response;

I did
$result->{'_raw_xml'} = $response;

The only reason being so that I can access the raw XML via the SOM object. Seems to work OK.

James

Thanks Byrne,
I modified SOAP::Lite and it works perfectly. But I did one thing differently.

Instead of
$self->{'_raw_xml'} = $response;

I did
$result->{'_raw_xml'} = $response;

gayani champika

i have written CGI Server that receives and returns attachments as a


#!/usr/bin/perl
use SOAP::Transport::HTTP;
use SOAP::MIME;

SOAP::Transport::HTTP::CGI
->dispatch_to('SOAP_MIME_Test')
->handle();

BEGIN {

package SOAP_MIME_Test;
use strict;
use vars qw(@ISA);
@ISA = qw(SOAP::Server::Parameters);

sub echo {
my $self = shift;
my $envelope = pop;
foreach my $part (@{$envelope->parts}) {
print STDERR "Attachments.cgi: attachment found! (".ref($$part).")\n";
}
print STDERR "envelope is of type: " . ref($envelope) . "\n";
my $STRING = $envelope->dataof("//echo/whatToEcho")
or die SOAP::Fault->faultcode("Client")
->faultstring("You must specify a string to echo")
->faultactor('urn:SOAP_MIME_Test#echo');

my $ent = build MIME::Entity
'Id' => "",
'Type' => "text/xml",
'Path' => "examples/attachments/some2.xml",
'Filename' => "some2.xml",
'Disposition' => "attachment";
return SOAP::Data->name("whatToEcho" => $STRING),$ent;
}

}

and receiving program as a


#!/usr/bin/perl
use SOAP::Lite;
use SOAP::MIME;

my $soap = SOAP::Lite
->readable(1)
->uri('http://localhost/SOAP_MIME_Test')
->proxy('http://localhost/cgi-bin/send6_attachment.cgi');

my $som = $soap->foo();

foreach my $part (@{$som->parts}) {
print $som->stringify;
}
but error is coming as a
Can't locate object method "parts" via package "SOAP::SOM" at /usr/local/apache/cgi-bin/send8_attachment.pl line 36.

pls help me

thanks
gayani

William

I'm curious. Why isn't there a native method for SOAP::SOM to extract the object into XML? This seems to be a popular desire from what I've seen while looking around the web, and yet it doesn't exist and writing an algorithm on top of SOAP::SOM is useless.

If I have some XML structure inside my SOAP body, I shouldn't have to know how it's structured in order to retrieve it from the SOAP object created by SOAP::SOM.

I've tried everything I can think of to extract the data within the object model, including trying to read $object->value() where there shouldn't be a value, catch $@, parse $@, and (if it's an unblessed reference error) utilize that knowledge to parse the object.

I could, I suppose, Dumper() the object and work off the internal representation, but while Perl often has many ways to do something, I don't consider that approach to be an acceptable one.

The simplest algorithm to build an XML document off a data structure (assuming simple Perl here, no objects) seems to be something like this:

get data structure root
parse(root)

parse:
if root.value is scalar, return(make one-line tag)
else return(make tag, parse(root.value), make closing tag)
end

Of course, since it appears that everything is internally represented as a SOAP::SOM object, since I can't test something's data type, since I shouldn't rely on $@, since I have no introspective methods at my disposal at all, indeed since SOAP::SOM is so primitive, I have nothing that works.

Any suggestions?

GrahamB

When I try to use Soap::Lite transport() method I get back the following

Can't locate object method "call" via package "SOAP::Transport::HTTP::Client" (perhaps you forgot to load "SOAP::Transport::HTTP::Client"?) at Soap04.pl line 74.


SOAP::Transport::HTTP::Client::send_receive: HTTP::Response=HASH(0x86a966c)
SOAP::Transport::HTTP::Client::send_receive: HTTP/1.1 500 Internal Server Error

Q. How can I modify the HTTP headers using transport so Content-ID is multipart/related.

Thanks,

Gb

Tom

Is it possible to retrieve the RAW xml result data? Without using outputxml?

Rohant

Hi
i have a simmilar problem
i am catching errors on client side with response-'gt;fault i get the faultcode and faultstring correctly as in soap message and is logged but the faultdetail is not getting retrieved. the tcpdump of the application run shows the faultdetail as follows soap:Server Application error CONNECT ERROR FATAL: Password authentication failed for user "test".
now the problem is how can i retreive the value of detail element i.e string "CONNECT ERROR FATAL: Password authentication failed for user 'test'. " as i want that also to be logged to error log but faultdetail returns undef/empty (i guess!) as its not a ref, i checked for ref. can any soap guru tell me the way to get it work
Rohant

Ales Tepina

With dataof you get a soap data object, so you can access the attributes, but what can you do, if you have more objects? Is it possible to move to the next object and then call dataof again?

I hope this makes sense.

Thanks for your replies.
Ales

shan

When i am running the bellow server code and and the client am getting internal server please anybody fix my problem....
server:
use SOAP::Lite +trace;
use SOAP::Transport::HTTP;
use DBI;
use DBD::mysql;

my $daemon = SOAP::Transport::HTTP::Daemon
-> new (LocalAddr=>'localhost', LocalPort=>xxxx, listen=>5);
$daemon -> dispatch_to('TodoData::createTodo');
print "Contact SOAP server at ", $daemon->url, "\n";
$daemon->handle();

Package TodoData;

sub getTodoList(acronym)
{
todoList()

$connect = Mysql->connect($dsn, 'student','1qaz2wsx');
$Mysql = "SELECT *from todo where user = '$acronym'";
$test=$connect->query($Mysql) ;
return ($test);
}

sub getTodoOneDay(acronym, date)
{

$dsn = "dbi:mysql:$database:localhost:xxxx";
$connect = DBI->connect($dsn, 'student','1qaz2wsx');
$sql = "SELECT * FROM todo WHERE user = '$_[0]' and date = '$_[1]'";
$new = $connect->query($sql);
return ($new);
}

sub createTodo()
{


$dsn = "dbi:mysql:$database:localhost:xxxx";
$connect = DBI->connect($dsn, 'student','1qaz2wsx');


$Mysql = "INSERT INTO $tablename (acronym, time, note, priority) VALUES ('$_[0]', '$_[1]', '$_[2]', '$_[3]')";

$execute=$connect->prepare($Mysql);
return ($execute);
return "INSERTED";

}


sub updateTodo()
{
$dsn = "dbi:mysql:$database:localhost:xxxx";
$connect = DBI->connect($dsn, 'student','1qaz2wsx');


$Mysql = "UPDATE INTO todo (id, acronym, time, note, priority) VALUES ('$_[0]', '$_[1]', '$_[2]', '$_[3]', '$_[4])";
$execute=$connect->prepare($Mysql);
return ($execute);
return "UPDATED";
}

sub deleteTodo()
{
$dsn = "dbi:mysql:$database:localhost:xxxx";
$connect = DBI->connect($dsn, 'student','1qaz2wsx');

$sql = "DELETE FROM todo WHERE user = '$_[0]' and id = '$_[1]'";
mysql_query($sql);
}

client:
#!/usr/bin/perl

use SOAP::Lite +trace;
my $client = SOAP::Lite
->proxy('http://localhost:xxxx')

->uri('http://xmlme.com/WebServices')
->on_action(sub { join '/', @_ })
->uri('toDoServer.Services#getTodoList')
->readable('true')
->default_ns('xs');
my $som = $client->call('createTodo',
SOAP::Data->name('acronym')
->value('xxxx'),SOAP::Data->name('time')->value('2010-05-28 12:00'),SOAP::Data->name('note')->value('I have exajm'),SOAP::Data->name('priority')->value('1'),


);

The comments to this entry are closed.