Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XML::Compile to generate sample XML from schema with enumeration and choice

Tags:

xml

perl

xsd

Following some existing tutorials, I created below perl script to generate XML from xsd.

Script:

#!/opt/perl/bin/perl -w
use warnings;
use strict;
use XML::Compile::Schema;
use XML::LibXML::Reader;
use XML::Compile::Util qw/pack_type/;

my $in_qfn  = $ARGV[0];
my $out_qfn = $ARGV[1];

open (OUT, ">$out_qfn") || die "Unable to create output file: $out_qfn\n";

# Generate the hash of xml
my $schema = XML::Compile::Schema->new($in_qfn);
my $type = pack_type 'urn:testsample','Document';
my $data = $schema->template('PERL', $type);
$data =~ s/#.*//g;
$data =~ s/\s*//g;
$data = eval($data);

# Print the xml
my $doc = XML::LibXML::Document->new('1.0','UTF-8');
my $write = $schema->compile(WRITER=>$type);
my $xml = $write->($doc,$data);
$doc->setDocumentElement($xml);
print OUT $doc->toString(1);

Input Schema:

<xs:schema xmlns="urn:testsample" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:testsample" elementFormDefault="qualified">
    <xs:element name="Document">
            <xs:complexType>
                    <xs:sequence>
                            <xs:element ref="address" minOccurs="1" maxOccurs="unbounded"/>
                    </xs:sequence>
            </xs:complexType>
    </xs:element>
    <xs:element name="address">
            <xs:complexType>
                    <xs:sequence>
                            <xs:element name="Pname" type="Pname" minOccurs="0" maxOccurs="1"/>
                            <xs:element ref="street" minOccurs="0" maxOccurs="3"/>
                            <xs:element name="contact" type="contacttype"/>
                    </xs:sequence>
            </xs:complexType>
    </xs:element>
    <xs:complexType name="contacttype">
            <xs:choice>
                    <xs:element ref="bday" minOccurs="0" maxOccurs="1"/>
                    <xs:element ref="email" minOccurs="0" maxOccurs="1"/>
                    <xs:element ref="postal" minOccurs="0" maxOccurs="2"/>
            </xs:choice>
    </xs:complexType>
    <xs:simpleType name="Pname">
            <xs:restriction base="xs:string">
                    <xs:enumeration value="AA"/>
                    <xs:enumeration value="BB"/>
            </xs:restriction>
    </xs:simpleType>
    <xs:element name="street" type="xs:string"/>
    <xs:element name="email" type="xs:string"/>
    <xs:element name="postal" type="xs:string"/>
    <xs:element name="bday" type="xs:date"/>
</xs:schema>

The output is correct. To take it further, in the example schema, there are choice elements, enumerations and repetitive elements. I would like to:

1. iterate all the possible values
2. generate maximum repetitive elements, if unbounded then 99
3. generate all choice values
4. split them into multiple XML files such that every XML is valid against the original schema. 

The output XML files amount is: i*j (i: the amount of choices in each node recursively, can be i1*i2*i3... j: the amount of enumerations in each node recursively, can be j1*j2*j3...)

In this case, it outputs 2*3=6 XML files (if there is an embedded node with 2 enumeration values, then it becomes 2*2*3=12 XML files), similar to:

XML file 1:

<x0:Document xmlns:x0="urn:testsample">
<x0:address>
    <x0:Pname>AA</x0:Pname>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:contact>
        <x0:email>example</x0:email>
    </x0:contact>
</x0:address>
</x0:Document>

XML File 2:

<x0:Document xmlns:x0="urn:testsample">
<x0:address>
    <x0:Pname>BB</x0:Pname>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:contact>
        <x0:email>example</x0:email>
    </x0:contact>
</x0:address>
</x0:Document>

XML File 3:

<x0:Document xmlns:x0="urn:testsample">
<x0:address>
    <x0:Pname>AA</x0:Pname>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:contact>
        <x0:bday>2017-10-2</x0:bday>
    </x0:contact>
</x0:address>
</x0:Document>

XML File 4:

<x0:Document xmlns:x0="urn:testsample">
<x0:address>
    <x0:Pname>BB</x0:Pname>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:contact>
        <x0:bday>2017-10-2</x0:bday>
    </x0:contact>
</x0:address>
</x0:Document>

XML File 5:

<x0:Document xmlns:x0="urn:testsample">
<x0:address>
    <x0:Pname>AA</x0:Pname>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:contact>
        <x0:postal>example</x0:postal>
        <x0:postal>example</x0:postal>
    </x0:contact>
</x0:address>
</x0:Document>

XML File 6:

<x0:Document xmlns:x0="urn:testsample">
<x0:address>
    <x0:Pname>BB</x0:Pname>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:street>example</x0:street>
    <x0:contact>
        <x0:postal>example</x0:postal>
        <x0:postal>example</x0:postal>
    </x0:contact>
</x0:address>
</x0:Document>

I firstly stuck to get the correct value for i and j, as they will be variables used in the generated XML file names, then the possible split. Any help is appreciated!

like image 420
dellair Avatar asked Jan 03 '23 09:01

dellair


1 Answers

(I am the author of XML::Compile) This can never lead to an generic solution, so it is probably not worth the effort...

Nearly all elements have different values which you need to test, enumeration seems to be special, but isn't. For instance, an integer is an enumeration as well. Would you walk all its values? The number of combinations explode.

When you want to collect more info from the schema than the template() function shows, try to extend XML::Compile::Translate::Template. It offers you details from the xsd in simple HASH structures.

Success

like image 188
Mark Overmeer Avatar answered Jan 14 '23 15:01

Mark Overmeer