From XML (schema) to Application Mark Overmeer MARKOV@cpan.org YAPC::EU 2008 2008-08-13 MARKOV Solutions http://perl.overmeer.net From XML (schema) to Application Mark Overmeer MARKOV@cpan.org YAPC::EU 2008 2008-08-13 I HATE XML MARKOV Solutions http://perl.overmeer.net Objective XML schema's are very popular Schema/XML complications name-spaces strongly typed verbose notation often generated Application complications message validation: strictness schema location schema versioning XML::Compile suite helps! 3 XML::Compile Advantages over other XML modules: very strict following the rules: no DWIM, no guessing usually no need for name-space understanding "slow" compile phase, then fast run very close to 100% support: no known bugs 4 XML::Compile Advantages over other XML modules: very strict following the rules: no DWIM, no guessing usually no need for name-space understanding "slow" compile phase, then fast run very close to 100% support: no known bugs my $schema = XML::Compile::Schema->new($xsdfile); 5 XML::Compile Advantages over other XML modules: very strict following the rules: no DWIM, no guessing usually no need for name-space understanding "slow" compile phase, then fast run very close to 100% support: no known bugs my $schema = XML::Compile::Schema->new($xsdfile); my $read = $schema->compile(READER => '{myns}mytype'); my $hash = $read->($xml); print Dumper $hash; 6 XML::Compile Advantages over other XML modules: very strict following the rules: no DWIM, no guessing usually no need for name-space understanding "slow" compile phase, then fast run very close to 100% support: no known bugs my $schema = XML::Compile::Schema->new($xsdfile); my $read = $schema->compile(READER => '{myns}mytype'); my $hash = $read->($xml); print Dumper $hash; my $doc = XML::LibXML::Document->new('1.0', 'UTF-8'); my $write = $schema->compile(WRITER => '{myns}mytype'); my $xml = $write->($doc, $hash); print $xml->toString(1); 7 XML::Compile::Schema Collects information from all used schema's Organizes compilation X::C::Translate, with back-ends X::C::T::Reader X::C::T::Writer X::C::T::Template XML → HASH HASH → XML example generator built-in types name-space administration Compilation results in code-refs 8 XML::Compile::Cache Extends X::C::Schema compiled code-ref management compile option management pre-compile for daemons or compile-on-demand single shot use of prefixes 9 XML::Compile::Cache Extends X::C::Schema compiled code-ref management compile option management pre-compile for daemons or compile-on-demand single shot use of prefixes my @p = (myprefix => $mynamespace); my $schema = XML::Compile::Cache->new(prefixes => \@p); my $type = 'myprefix:mylocal'; # {mynamespace}mylocal $schema->declare(READER => $type, @options); # $schema->compileAll my $data = $schema->reader($type)->($xml); my $xml = $schema->writer($type)->($doc, $data); 10 Other modules for XML::Compile XML::Compile::SOAP11 XML::Compile::WSDL11 my $wsdl = XML::Compile::WSDL11->new($wsdlfile); my $call = $wsdl->compileClient('CallName'); my ($answer, $trace) = $call->($request); 11 Other modules for XML::Compile XML::Compile::SOAP11 XML::Compile::WSDL11 my $wsdl = XML::Compile::WSDL11->new($wsdlfile); my $call = $wsdl->compileClient('CallName'); my ($answer, $trace) = $call->($request); XML::Compile::SOAP::Daemon my $daemon = XML::Compile::SOAP::HTTPDaemon->new; $daemon->operationsFromWSDL($wsdl, handlers => { CallName => \&handle_call } ); $daemon->run; based on Net::Server and HTTP::Daemon 12 Other modules for XML::Compile XML::Compile::Tester XML::Compile::Dumper XML::Compile::Rewrite xmlrewrite --plugin schema2001 --xmlns --no-annotations --no-comments --no-id-constraints --expand-includes --output xsd=$SCHEMA2001,me=$OTHER reformated.xsd To come: --no-elements version,bigone --promote-unnamed 2 --sort name --extract-element --no-unused-types ... etc ... XML::LibXML::Simple 13 Publish XML Interface Module based on XML::Compile::Cache Include all versions of the schema in the package Collect validated message examples create readers for them, and study the result which types are really used? (what to declare) Create constants for name-spaces 14 Example: Geo::KML Geo::KML::Util package Geo::KML::Util; use base 'Exporter'; my @kml21 = qw/NS_KML_21/; my @kml220 = qw/NS_KML_22 NS_ATOM_2005 NS_XAL_20/; my @EXPORT = (@kml21, @kml220); my %EXPORT_TAGS = (kml21 => \@kml21, kml220 => \@kml22); use constant NS_KML_21 => 'http://earth.google.com/kml/2.1'; use constant NS_KML_22 => 'http://www.opengis.net/kml/2.2'; use constant NS_ATOM_2005 => 'http://www.w3.org/2005/Atom'; use constant NS_XAL_20 => 'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'; 1; 15 Example: Geo::KML Protocol version package Geo::KML; use base 'XML::Compile::Cache'; use Geo::KML::Util; # all constants my %ns2version = ( &NS_KML_21 => '2.1' , &NS_KML_22 => '2.2.0'); my %version2ns = reverse %ns2version; # ::Cache::new { (bless {},$class)->init(\%args) } sub init($) { my ($self, $args) = @_; my $version = $args->{version} or die; $class->SUPER::init($args); ... use Geo::KML; my $kml = Geo::KML->new(version => '2.2.0'); 16 Example: Geo::KML Version specifics my %info = ( '2.1' => { prefixes => [ '' => NS_KML_21 ] , schemas => [ 'kml-2.1/*.xsd' ] } , '2.2.0' => { prefixes => [ '' => NS_KML_220, atom => NS_ATOM_2005 , xal => NS_XAL_20 ] , schemas => [ 'kml-2.2.0/*.xsd', 'atom-2005/*.xsd' , 'xal-2.0/*.xsd' ] } ); sub init($) { ... my $info = $info{$version} or die; $args->{prefixes} = $info->{prefixes}; $self->SUPER::init($args); ... 17 Example: Geo::KML Compile schema's $info{'2.2.0'}{schemas} = ['kml-2.2.0/*.xsd', ...]; sub init($) { ... $self->SUPER::init($args); (my $xsd = __FILE__) =~ s!\.pm$!/xsd!; my $patterns = $info{$version}{schemas}; my @xsds = map {glob "$xsd/$_"} @$patterns; $self->importDefinitions(\@xsds); lib/Geo/KML.pm lib/Geo/KML/Util.pm lib/Geo/KML/xsd/atom-2005/atom-author-link.xsd lib/Geo/KML/xsd/kml-2.2.0/ogckml22.xsd lib/Geo/KML/xsd/xal-2.0/xAL.xsd 18 Example: Geo::KML Read KML sub init($) { ... $self->declare(READER => 'kml'); $self; } name-space qualified! (empty prefix) sub readKML(@) { my ($class, $xml, %opts) = @_; $xml->nodeName eq 'kml' or die; my $obj = $class->new(version => $xml->namespaceURI); my $data = $obj->reader('kml')->($xml); return $data; use Geo::KML; my ($type, $data) = Geo::KML->readKML('points.kmz'); 19 Example: Geo::KML Write KML $self->declare(WRITER => 'kml', include_namespaces => 1); sub writeKML($$) { my ($self, $data, $filename) = @_; my $doc = XML::LibXML::Document->new('1.0', 'UTF-8'); my $xml = $self->writer('kml')->($doc, $data); $doc->setDocumentElement($xml); $doc->toFile($filename, 1); } 20 Example: Geo::KML Write KML use Geo::KML; my %location = ( name => '' , description => '' , Point => {coordinates => [ "$long,$lat,0" ] } ); my %data = ( AbstractFeatureGroup => [ { Placemark => \%location }, ] ); my $kml = Geo::KML->new(version => '2.2.0'); $kml->writeKML(\%data, 'example.kml'); 21 Example: Geo::KML ... and then ... simplify access to the data-structure my %typemap = ( "{$kml22}Document" => Geo::KML::Document ); $self->declare(RW => 'kml', typemap => \%typemap); package Geo::KML::Document; # shows default implem. sub toXML($$) { my ($self, $type, $doc) = @_; $self; # XML data-structure or XML node } sub fromXML($$) { my ($class, $data, $type) = @_; bless $data, $class; } 22