http://qs321.pair.com?node_id=694629
Category: Text Processing
Author/Contact Info Ayhan Ulusoy <dev@ulusoy.name>
Description:

Hello all,

Having just released the first available version (v0.52) of XML::Pastor, I have found this discussion list that might best suit speaking about it.

Let me run the commercial....

Now you don't need to write code in an ugly language like Java in order to be able to get native XML support. Because now, there is XML::Pastor.

XML::Pastor is a revolutionary new way to handle XML documents in Perl.

Give it a try. You won't regret it. Guaranteed (or almost).

In fact, if you are familiar with Java's XML CASTOR, this module (XML::Pastor) will be very familiar to you. XML::Pastor is very similar to Java's Castor, however, as usual with Perl, it's more flexible. On the other hand, full XSD support is not achieved yet (a lot is already supported, see below).

XML::Pastor will actually generate Perl code starting with one or more W3C Schema(s) (XSD). The generated code is as easy, if not easier, to use as XML::Simple. YET (and this is the tricky part), you can also easily read and write to and from an XML document ('instance' of the schema) abiding by the rules of the schema. You can even validate against the original schema before writing the XML document.

However, you don't need the original schema at run-time (unless you are doing run-time code generation). Everything is translated into Perl at code generation time. Your generated classes 'know' about how to read, write, and validate XML data against the rules of the original schema without the actual schema at hand.

Attributes and child elements can be accessed through auto-generated accessors or hash items. You don't need to know or worry about whether or not a child element appears once or multiple times in the XML document. This is automagically taken care of for you. If you access the child element as an array (with a subscript), it is OK. But you don't need to. You might as well access the first such element directly, without needing to know that there are others.

Code can be generated at 'make' time onto disk (the so-called 'offline' mode) or it can be generated and 'eval'ed for you as a big chunk at run-time. Or you can get it as a string ready to be evaled. In 'offline' mode, you can choose to use a 'single' module style (where all code is in one big chunk), or in 'multiple' style, where each class is written to dik in a separate module.

There is also a command line utility, called 'pastorize' that helps you do this from within 'make' files.

Gone with the multiplicity problem of XML::Simple. Gone with the complexity of dealing with XML as XML. Now you can deal with XML data as real Perl objects (with accessors for child elements and attributes). But you can still get XML back out of it at the end of the day.

W3C SCHEMA SUPPORT

Most of the W3C XSD Schema structures are supported. Notable exception is substitution sets. Namespace support is currently a bit shaky (at most one namespace per generation). That's why schema 'import' is not - yet- supported. However, schema 'include' and 'replace' are supported.

All W3C schema builtin types have Perl counterparts with validation. This includes datetime and date types as well (You can get/set these with CPAN's DateTime objects as well).

Internally, XML::Pastor uses XML::LibXML to deal with actuial xml reading/writing XML (but not for validation). But, you don't need to know anything about XML::LibXML for being able to use XML::Pastor.

Give it a try please. And enjoy...

Note: It's already on CPAN. Just search for XML::Pastor on search.cpan.org.

Cheers,

Ayhan Ulusoy

  use XML::Pastor;
   
  my $pastor = XML::Pastor->new();

  # Generate MULTIPLE modules, one module for each class, and put them
+ under destination.  
      
  $pastor->generate(    
              mode =>'offline',
              style => 'multiple',
            schema=>'/some/path/to/schema.xsd', 
            class_prefix=>'MyApp::Data::',
            destination=>'/tmp/lib/perl/', 
            );  


  # Generate a SINGLE module which contains all the classes and put it
+ under destination.    
  # Note that the schema may be read from a URL too.
  
  $pastor->generate(    
              mode =>'offline',
              style => 'single',
            schema=>'http://some/url/to/schema.xsd', 
            class_prefix=>'MyApp::Data::',
            module => 'Module',
            destination=>'/tmp/lib/perl/',                            
+ 
            );  


  # Generate classes in MEMORY, and EVALUATE the generated code on the
+ fly.
  # (Run Time code generation)
  
    $pastor->generate(    
            mode =>'eval',
            schema=>'/some/path/to/schema.xsd', 
            class_prefix=>'MyApp::Data::'
            );  
  

  # Same thing, with a maximum of DEBUG output on STDERR
 
    $pastor->generate(    
            mode =>'eval',
            schema=>'/some/path/to/schema.xsd', 
            class_prefix=>'MyApp::Data::',
            verbose = 9
            );

And somewhere in an other place of the code ... (Assuming a global XML element 'country' existed in you schema and hence been generated by Pastor).

  my $country = MyApp::Data::country->from_xml_file('/some/path/to/cou
+ntry.xml');    # retrieve from a file    
  $country = MyApp::Data::country->from_xml_url('http://some/url/to/co
+untry.xml');     # or from a URL
  $country = MyApp::Data::country->from_xml_fh($fh);     # or from a f
+ile handle  
  $country = MyApp::Data::country->from_xml_dom($dom);    # or from DO
+M  (a XML::LibXML::Node or XML::LibXML::Document)

  # or from an XML string
  $country = MyApp::Data::country->from_xml_string(<<'EOF');
  
  <?xml version="1.0"?>
  <country code="FR" name="France">
    <city code="PAR" name="Paris"/>
    <city code="LYO" name="Lyon"/>    
  </country>
  EOF
  
  # or if you don't know if you have a file, URL, FH, or string
  $country = MyApp::Data::country->from_xml('http://some/url/to/countr
+y.xml');
  
  
  # Now you can manipulate your country object.  
  print $country->name;                    # prints "France"
  print $country->city->[0]->name;        # prints "Paris"
  
  
  # Let's make some changes  
  $country->code('fr');
  $country->name('FRANCE');
  
  my $class=$country->xml_field_class('city');
  my $city = $class->new();
  $city->code('MRS');
  $city->name('Marseille');
  
  push @{$country->city}, $city;
  
  print $country->city->[2]->name;    # prints "Marseille"
  
  # Time to validate our XML
  $country->xml_validate();            # This one will DIE on failure
  
  if ($country->is_xml_valid()) {    # This one will not die.
    print "ok\n";
  }else {
    print "Validation error : $@\n";  # Note that $@ contains the erro
+r message
  }  
  
  # Time to write the the object back to XML
  $country->to_xml_file('some/path/to/country.xml');        # To a fil
+e  
  $country->to_xml_url('http://some/url/to/country.xml');    # To a UR
+L  
  $country->to_xml_fh($fh);                    # To a FILE HANDLE  

  my $dom=$country->to_xml_dom();            # To a DOM Node (XML::Lib
+XML::Node)
  my $dom=$country->to_xml_dom_document();    # To a DOM Document  (XM
+L::LibXML::Document)
  my $xml=$country->to_xml_string();        # To a string  
  my $frag=$country->to_xml_fragment();        # Same thing without th
+e <?xml version="1.0?> part