XML ::编译以使用枚举和选择从模式生成示例XML

时间:2017-10-02 10:00:26

标签: xml perl xsd

在一些现有教程之后,我创建了下面的perl脚本来从xsd生成XML。

脚本:

#!/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);

输入架构:

<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>

输出正确。更进一步,在示例模式中,有选择元素,枚举和重复元素。我想:

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. 

输出XML文件的数量是:i * j(i:递归地每个节点中的选择量,可以是i1 * i2 * i3 ... j:递归地每个节点中的枚举量,可以是j1 * J2 * J3 ...)

在这种情况下,它输出2 * 3 = 6个XML文件(如果有一个嵌入节点有2个枚举值,则它变为2 * 2 * 3 = 12个XML文件),类似于:

XML文件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文件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文件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文件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文件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文件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和j获取正确的值,因为它们将是生成的XML文件名中使用的变量,然后是可能的拆分。任何帮助表示赞赏!

2 个答案:

答案 0 :(得分:6)

(我是XML :: Compile的作者) 这永远不会导致通用解决方案,因此可能不值得努力......

几乎所有元素都有不同的值,您需要测试,枚举似乎是特殊的,但不是。例如,整数也是枚举。你会全力以赴吗?组合数量爆炸。

如果要从模式中收集比template()函数显示的更多信息,请尝试扩展XML :: Compile :: Translate :: Template。它以简单的HASH结构为您提供xsd的详细信息。

成功

答案 1 :(得分:3)

您似乎没有掌握所涉及数字的大小。

让我添加一些限制:

  • 我们将有限重复限制为minmin+1max-1max次重复的四个测试用例。
  • 我们将无限重复限制为三个minmin+1min+100次重复的测试用例。
  • 我们始终对xs:stringxs:date使用相同的值。

即使有这些限制,如果我们减少无限重复的第三种情况,你的简单模式将导致4×10 179 组合(或4×10 19 组合从min+100min+10)。

为了了解这些数字的规模,请考虑这一点:可观察宇宙中只有10个 80 原子。

通用解决方案是不可能的。

使用以下程序获得该数字:

use strict;
use warnings qw( all );
use feature qw( say );

sub sum { my $acc = 0; $acc += $_ for @_; $acc }
sub mul { my $acc = 1; $acc *= $_ for @_; $acc }

sub choice { sum @_ }
sub sequence { mul @_ }

sub repeat {
   my ($x, $min, $max) = @_;
   my @exps;
   if (defined($max)) {
      @exps = ( $min, $min+1, $max-1, $max );
      my $diff = $max - $min;
      die if $diff < 0;
      splice @exps, $diff+1 if $diff < 3;
   } else {
      @exps = ( $min, $min+1, $min+100 );   
   }

   return choice map { $x ** $_ } @exps;
}

# optional($x) is a shortcut for repeat($x, 0, 1)
sub optional { 1 + $_[0] }

sub Document { repeat(address(), 1) }
sub address { sequence optional(Pname()), repeat(street(), 0, 3), contacttype() }
sub contacttype { optional(contacttype_choice()) }
sub contacttype_choice { choice bday(), email(), repeat(postal(), 1, 2) }
sub Pname { 2 }
sub street { 1 }
sub bday { 1 }
sub email { 1 }
sub postal { 1 }

say Document();

上述程序使用以下更简单但等效的contacttype定义:

<xs:complexType name="contacttype">
        <xs:choice minOccurs="0" maxOccurs="1">
                <xs:element ref="bday"/>
                <xs:element ref="email"/>
                <xs:element ref="postal" minOccurs="1" maxOccurs="2"/>
        </xs:choice>
</xs:complexType>