At first big thanks to Mark Overmeer for XML::Compile. I had the pleasure to meet Mark in .nl once – cheers and carry on the great work.
Whenever I had to create XML (either with perl or php) I did it one way or the other with some sort of templating toolkit. In php I use(d) http://www.tinybutstrong.com/ for creating xml . In perl several templating systems come into mind like
tt2
Template::Declare
and many others more
Feeling Wrong
this always felt wrong and awkward for several reasons:
You need to build/have the xml before (and creating xml from xsd is not one of my many hobbies)
You/the templating system have to take care whether or not a whole tree needs to be displayed and so on
This nifty Module comes to the rescue casino luck. Using XML::Compile make xml+xsd behave like I always wanted to.
I want to demonstrate this using epp as an example. I want to create a valid epp frame for creating a contact object.
The epp schemas may be obtained by a simple internet search eg here.
You may create a valid epp frame by reading the both the contact-1.0.xsd and the epp-1.0.xsd each approx. 400 lines of thrilling xml. Or use some code like:
1. Convert xsd to perl hash
use strict;
use warnings;
use XML::Compile::Schema;
my $schema = XML::Compile::Schema->new([
'epp-xsd/epp-1.0.xsd',
'epp-xsd/eppcom-1.0.xsd',
'epp-xsd/contact-1.0.xsd',
]);
my $s = $schema->template('PERL' => '{urn:ietf:params:xml:ns:contact-1.0}create');
print $s;
which gives you a more readable idea what your data should look like:
# is a x0:createType
{ # sequence of id, postalInfo, voice, fax, email, authInfo, disclose
# is a xs:token
# length <= 16
# length >= 3
id => "token",
# is a x0:postalInfoType
# occurs 1 <= # <= 2 times
postalInfo =>
[ { # sequence of name, org, addr
# is a xs:normalizedString
# length <= 255
# length >= 1
name => "example",
....
Using this perl hash template you create the first part of your epp-xml
2. Use perl hash to xml conversion
use strict;
use warnings;
use XML::Compile::Schema;
my $schema = XML::Compile::Schema->new([
'epp-xsd/epp-1.0.xsd',
'epp-xsd/eppcom-1.0.xsd',
'epp-xsd/contact-1.0.xsd'
]);
my $write = $schema->compile(WRITER => '{urn:ietf:params:xml:ns:contact-1.0}create');
my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
my $hash = {
id => 'idid',
postalInfo => {
'name' => 'name',
'addr' => {
'street' => ['street', 'street2'],
'city' => 'city',
'cc' => 'cc',
},
type => 'int',
},
email => 'mymail',
"authInfo" => {pw => "PWauthInfo"},
};
my $xml = $write->($doc, $hash);
$doc->setDocumentElement($xml);
print $doc->toString(1);
this leads to the following xml
idid
name
street
street2
city
cc
mymail
PWauthInfo
this xml has to be wrapped into an epp frame. As the epp frame uses xml any elements some “manual” work is necessary for creating the complete epp frame. In principle you start with {urn:ietf:params:xml:ns:epp-1.0}epp at step one.
3. Generate epp-xml-frame and wrap contact-create command into it
use strict;
use warnings;
use XML::Compile::Cache;
use XML::Compile::Schema;
my $cache = XML::Compile::Cache->new([
'epp-xsd/eppcom-1.0.xsd',
'epp-xsd/epp-1.0.xsd',
'epp-xsd/extensions.xsd',
'epp-xsd/contact-1.0.xsd',
]);
my $create_contact_ns = '{urn:ietf:params:xml:ns:contact-1.0}create';
my $epp_frame_ns = '{urn:ietf:params:xml:ns:epp-1.0}epp';
my $prefixes = {'urn:ietf:params:xml:ns:contact-1.0' => 'contact'};
$cache->declare(WRITER => [$create_contact_ns, $epp_frame_ns, ],
(
prefixes => $prefixes,
use_default_namespace => 1,
include_namespaces => 1,
)
);
$cache->compileAll;
my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
$doc->setStandalone(0);
my $contact_data = {
id => 'idid',
postalInfo => {
'name' => 'name',
'addr' => {
'street' => ['street', 'street2'],
'city' => 'city',
'cc' => 'cc',
},
type => 'int',
},
email => 'mymail',
"authInfo" => {pw => "PWauthInfo"},
};
my $xml = $cache->writer($create_contact_ns)->($doc, $contact_data);
my $epp_frame_data = {
command => {
create => {
'{urn:ietf:params:xml:ns:contact-1.0}create' => $xml,
},
clTRID => "token",
},
};
my $eppxml = $cache->writer($epp_frame_ns)->($doc, $epp_frame_data);
$eppxml->setNamespace( 'http://www.w3.org/2001/XMLSchema-instance', 'xsi', 0 ); ## append additonal ns for the feelinx
print $doc->toString(1) .
$eppxml->toString(1) ."\n";
Voila, we have a valid epp frame without even touching the xml! Again Kudos to Mark Overmeer for making this possible!
4. The result
idid
name
street
street2
city
cc
mymail
PWauthInfo
token