Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to use some web services via SOAP::Lite and for the most part it has been going smoothly. But now I'm beginning to hit problems with the fast and loose way that perl turns the results into perl structures. I'd rather be returning nicely packaged objects to the user rather than nested hashes. And I'd love not to code such classes by hand if it can be helped

The WSDL defines all the data structures that are used, and in some other languages (for example java), you can generate classes to mirror the complex types defined there. But I haven't found anything in the perl world to do this. stubmaker.pl in the SOAP::Lite package just deals with the connection details and a few details of typing data you send. SOAP::WSDL seems to do a better job of typing the data I need to send. But neither of these do any autogeneration of classes to hold the data I get back from a call.

Is there anything out there that can take a WSDL file and autogenerate some classes to mirror the complex types it defines? Bonus point if the same classes can be used to easily wrap up the data in SOAP::Data objects to send
  • Comment on auto generating wrapper classes for complex types defined in WSDL

Replies are listed 'Best First'.
Re: auto generating wrapper classes for complex types defined in WSDL
by erroneousBollock (Curate) on Oct 11, 2007 at 06:30 UTC
    Please don't construct complexType's by hand. Let the WSDL do it for you. I repeat: Never, ever use SOAP::Data manually; it's not worth it.

    SOAP::WSDL (which is a sub-class of the client portion of SOAP::Lite) does what you want.

    It constructs the classes based on complexType definitions in the WSDL. It can be told to cache the created classes to the filesystem, so you can extract them and add meta-functionality yourself. If properly named classes are found in your @INC they can be used to instantiate complex return values from a service call.

    Version 2 of SOAP::WSDL is shaping up to be very flexible.

    -David

      I will have to look more closely at SOAP::WSDL. As far as I can tell, it does all the correct encoding of stuff I send to the server and uses the WSDL to do the right thing (and I applauded it for doing so). But the return values are still just autotyped and stuffed into nested hashes by SOAP::Lite. If you look at the call() subroutine in the SOAP::WSDL module, it looks like it wraps ups all of the parameters nice, then calls SUPER::call and returns the results without even looking at them. Nothing special is done to the return value as far as I can tell, which is specifically what I'm looking for. If I'm missing something about how SOAP::WSDL deals with the values returned by the soap calls, please point it out to me.
Re: auto generating wrapper classes for complex types defined in WSDL
by kyle (Abbot) on Oct 11, 2007 at 01:58 UTC

    Why do you want classes instead of data structures? If all you want is an object with get/set methods, I don't see how that's all that different from a structure with getable/setable key-value pairs. An object would prevent the user from accidentally using some wrong name (i.e., it prevents typos), but otherwise you're just giving them a structure with more access overhead.

    That said, generating a simple object from a hash is not that hard.

    use strict; use warnings; sub hash2obj { my $hash_ref = shift; my $package = shift; bless $hash_ref => $package; foreach my $key ( keys %{$hash_ref} ) { no strict 'refs'; *{$package.'::get_'.$key} = sub { $_[0]->{$key} }; *{$package.'::set_'.$key} = sub { $_[0]->{$key} = $_[1] }; } return $hash_ref; } use Test::More 'tests' => 8; my $t1 = { a => 1, b => 2 }; hash2obj( $t1, 'Test::One' ); isa_ok( $t1, 'Test::One' ); can_ok( $t1, 'set_a' ); can_ok( $t1, 'set_b' ); can_ok( $t1, 'get_a' ); can_ok( $t1, 'get_b' ); is( $t1->{a}, $t1->get_a(), 'get_a same as $t1->{a}' ); $t1->set_a(3); is( $t1->get_a(), 3, 'a is 3 after set' ); is( $t1->{a}, $t1->get_a(), 'get_a same as $t1->{a} after set' );

    This has some caveats:

      Yes, I could create the classes by hand, and that is what I may end up doing. But I would want more than just get/set methods. I'd love to have type checking and proper interpretation of the data that came from the server.

      I'd rather have this autogenerated. All the data is in the WSDL, so I shouldn't have to redo it all.

      Let me explain one problem I've seen a few times which is one of the reasons that pushed over over the edge from being happy with hashes to wanting classes.

      For this example, lets say I'm talking to a bug tracking system and the call returns a bug. Each bug can have 0 or more comments. If there is no comments the XML that comes back looking like

      <Commments></Comments>
      
      In which case SOAP::Lite decodes this as a string. If I get one:
      <Commments><Comment>blah</Comment></Comments>
      
      Which means Comments points to a hash {Comment => 'blah' }. If I have multiple comments:
      <Commments><Comment>blah</Comment><Comment>foo</Comment></Comments>
      
      Then I get Comments pointing to an array of hashes.

      This is annoying for end users to deal with. The WSDL defines the Comments type as an array of Comment types, and the user expects it to be an array even if there is only one or zero elements.

      And yes, I could write my classes to deal with this, but I'd rather not have too. If I was doing a lot of these, I'd definitely want an automated tool.
Re: auto generating wrapper classes for complex types defined in WSDL
by perlfan (Parson) on Oct 10, 2007 at 21:30 UTC
    I was going to mention stubmaker.pl, but you said you wanted something more. If you go through examples that http://www.soaplite.com/ provides, you'll see it is actually pretty straightforward to handle this on your own. The site is actually pretty good about docs.
      FYI, I posted the original question, but cookie problems conspired to make it post as anon. Yes, stubmaker just generates a wrapper to make the calls easier. The results still just returns nested hashes for complex data types. I'm looking for something that would generate classes that could be used instead of the nested hashes. One class per complex type with get/set methods to access the members of that complex type. Or something like that. I haven't been able to find anything like this among the SOAP::Lite docs. I hadn't seen the soaplite.com site before, but a quick glance doesn't find anything new. I will certainly browse some more to see if there is something I can use.
Re: auto generating wrapper classes for complex types defined in WSDL
by Cop (Initiate) on Oct 11, 2007 at 02:25 UTC

    I understand where you come from. It will be nice to have classes generated, so you don't need to care how the data is physcally organized, and only focus on the logical structure that's expressed in WSDL.

    But on the other hand, hash won't create too much trouble for you either, and most Perl classes are hashes and way.