NAME

SOAP::Data::ComplexType - An easy interface for creating and implementing infinitely complex SOAP::Data objects

SYNOPSIS

package My::SOAP::Data::ComplexType::Foo;
use strict;
use warnings;
use SOAP::Data::ComplexType;
use vars qw(@ISA);
@ISA = qw(SOAP::Data::ComplexType);

use constant OBJ_URI    => 'http://foo.bar.baz';
use constant OBJ_TYPE   => 'ns1:myFoo';
use constant OBJ_FIELDS => {
	field1              => ['string', undef, undef],
	field2              => ['int', undef, undef],
	field3              => ['xsd:dateTime', undef, undef]
};

sub new {
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $data = shift;
	my $obj_fields = shift;
	$obj_fields = defined $obj_fields && ref($obj_fields) eq 'HASH' ? {%{$obj_fields}, %{+OBJ_FIELDS}} : OBJ_FIELDS;
	my $self = $class->SUPER::new($data, $obj_fields);
	return bless($self, $class);
}

package My::SOAP::Data::ComplexType::Bar;
use strict;
use warnings;
use SOAP::Data::ComplexType;
use vars qw(@ISA);
@ISA = qw(SOAP::Data::ComplexType);

use constant OBJ_URI    => 'http://bar.baz.uri';
use constant OBJ_TYPE   => 'ns1:myBar';
use constant OBJ_FIELDS => {
	val1                => ['string', undef, undef],
	val2                => [
		[
			My::SOAP::Data::ComplexType::Foo::OBJ_TYPE,
			My::SOAP::Data::ComplexType::Foo::OBJ_FIELDS
		],
		My::SOAP::Data::ComplexType::Foo::OBJ_URI, undef
	]
};

sub new {
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $data = shift;
	my $obj_fields = shift;
	$obj_fields = defined $obj_fields && ref($obj_fields) eq 'HASH' ? {%{$obj_fields}, %{+OBJ_FIELDS}} : OBJ_FIELDS;
	my $self = $class->SUPER::new($data, $obj_fields);
	return bless($self, $class);
}

########################################################################
package main;

my $request_obj = My::SOAP::Data::ComplexType::Bar->new({
	val1    => 'sometext',
	val2    => {
		field1  => 'moretext',
		field2  => 12345,
		field3  => '2005-10-26T12:00:00.000Z'
	}
});
print $request_obj->as_xml_data;

use SOAP::Lite;
my $result = SOAP::Lite
		->uri($uri)
		->proxy($proxy)
		->somemethod(\SOAP::Data->value($request_obj->as_soap_data))
		->result;
		
#assuming the method returns an object of type Foo...
if (ref($result) eq 'Foo') {
	my $result_obj = My::SOAP::Data::ComplexType::Foo->new($result);
	print "$_=".$result_obj->$_."\n" foreach keys %{+My::SOAP::Data::ComplexType::Foo::OBJ_FIELDS};
}

ABSTRACT

SOAP::Data::ComplexType defines a structured interface to implement classes that represent infinitely complex SOAP::Data objects. Object instances can dynamically generate complex SOAP::Data structures or pure XML as needed. Fields of an object may be easily accessed by making a method call with name of the field as the method, and field values can be changed after object construction by using the same method with one parameter.

Blessed objects returned by a SOAP::Lite method's SOAP::SOM->result may be used to reconstitute the object back into an equivalent ComplexType, thus solving shortcomings of SOAP::Lite's handling of complex types and allowing users to access their objects in a much more abstract and intuive way. This is also exceptionally useful for applications that need use SOAP result objects in future SOAP calls.

DESCRIPTION

This module is intended to make it much easier to create complex SOAP::Data objects in an object-oriented class-structure, as users of SOAP::Lite must currently craft SOAP data structures manually. It uses SOAP::Data::Builder internally to store and generate object data.

I hope this module will greatly improve productivity of any SOAP::Lite programmer, especially those that deal with many complex datatypes or work with SOAP apps that implement inheritance.

IMPLEMENTATION

Creating a SOAP ComplexType class

Every class must define the following compile-time constants:
	OBJ_URI:   URI specific to this complex type
	OBJ_TYPE:  namespace and type of the complexType (formatted like 'myNamespace1:myDataType')
	OBJ_FIELDS: hashref containing name => arrayref pairs; see L<ComplexType field definitions>
	
When creating your constructor, if you plan to support inheritance, you must perform the following action:

	my $obj_fields = $_[1];	#second param from untouched @_
	$obj_fields = defined $obj_fields && ref($obj_fields) eq 'HASH' ? {%{$obj_fields}, %{+OBJ_FIELDS}} : OBJ_FIELDS;
	my $self = $class->SUPER::new($data, $obj_fields);
	
which insures that you support child class fields and pass a combination of them and your fields to
the base constructor.  Otherwise, you can simply do the following:

	my $self = $class->SUPER::new($data, OBJ_FIELDS);
	
(Author's Note: I don't like this kludgy constructor design, and will likely change it in a future release)

ComplexType field definitions

When defining a ComplexType field's arrayref properties, there are 4 values you must specify within an arrayref:
	type: (simple) SOAP primitive datatype, OR (complex) arrayref with [type, fields] referencing another ComplexType
	uri:  specific to this field
	attr: hashref containing any other SOAP::Data attributes
	
So, for example, given a complexType 'Foo' with 
	object uri='http://foo.bar.baz', 
	object type='ns1:myFoo'

and two fields (both using simple SOAP type formats)
	field1: type=string, uri=undef, attr=undef
	field2: type=int, uri=undef, attr=undef
	
we would define our class exactly as seen in the L<SYNOPSYS> for
package My::SOAP::Data::ComplexType::Foo.


The second form of the type field may be an arrayref with the following elements:
	type
	fields hashref
	
So, for example, given a complexType 'Bar' with
	object uri='http://bar.baz.uri', 
	object type='ns1:myBar'

and two fields (one using simple SOAP type, the other using complexType 'myFoo')
	field1: type=string, uri=undef, attr=undef
	field2: type=myFoo, uri=undef, attr=undef

we would define our class exactly as seen in the L<SYNOPSYS> for
package My::SOAP::Data::ComplexType::Bar.

Class Methods

My::SOAP::Data::ComplexType::Example->new( HASH )

	Constructor.  Expects HASH ref (or reference to blessed SOAP::SOM->result object).

Object Methods

$obj->as_soap_data

	Returns all data as a list of SOAP::Data objects.

$obj->as_xml_data

	Returns all data formatted as an XML string.

$obj->FIELDNAME( NEW_VALUE )

	Returns (or sets) the value of the given FIELDNAME field in your object. 
	NEW_VALUE is optional, and changes the current value of the object.

TODO

Changing the value of a field should also be able to support complex data structures, correctly imported into the complex type definition. Currently, only simple scalar values are supported.

Support for more properties of a SOAP::Data object. Currently only type, uri, attributes, and value are supported.

A WSDL (and perhaps even an ASMX) parser may be included in the future to auto-generate ComplexType classes, thus eliminating nearly all the usual grunt effort of integrating a Perl application with complex applications running under modern SOAP services such as Apache Axis or Microsoft .NET.

Add a test suite.

Improve on this documentation.

CAVIATS

The OBJ_FIELD data structure may change in future versions to more cleanly support SOAP::Data parameters. For now, I plan to keep it an array reference and simply append on new SOAP::Data parameters as they are implemented. Accessor methods may change as well, as the current interface is a little weak--it only returns first matched occurance of an element in the tree if there are multiple same-named elements.

BUGS

None known at this time. Bug reports and design suggestions are always welcome.

AUTHOR

Eric Rybski

COPYRIGHT AND LICENSE

Copyright 2005 by Eric Rybski, All Rights Reserved

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

SEE ALSO

SOAP::Lite SOAP::Data::Builder