这是我的情景:
我需要通过Perl生成XML,其中架构中充满了<xs:sequence>
个标签(即标签必须按顺序出现)。我无法控制架构(第三方),每当我们添加新的CPAN模块(没有很好的方式将它们传播给客户等)时,我们已经基本上禁止这些模块出现了很多问题。添加任何新内容(例如XML::Writer
)。
我可以使用的XML模块包括:XML::Parser
,XML::Simple
,XML::XPath
。
我非常喜欢在XML::Simple
中创建hashref w / hash / arary refs数据结构然后只是吐出XML的方式。
无论如何都要使用XML::Simple
执行此操作?或者也许滚动我自己的代码按顺序吐出XML?似乎我最大的问题是我需要以插入顺序从hashref中提取内容,而Perl并没有真正做到这一切。我确实读过关于Tie::IxHash
的问题,因为它是按照插入顺序提取的,但同样是我没有的模块。
感觉我有点像SOL,但肯定会感谢有人可能拥有的任何技巧/想法。感谢。
答案 0 :(得分:11)
大部分时间配置只需使用XML::Simple
提供的选项即可完成。通常情况下,它会自动将数据折叠成数据结构的简单数据,可以逻辑再现;它非常适合数据存储格式,但在匹配文档格式方面却不那么强大。幸运的是,即使它“简单”,它也非常强大。
要控制元素输出的顺序,您有几个选项。您可以使用数组,它保证数据的顺序。但是,看起来您需要一个标签的特定值顺序。
键排序也是一种自动功能。只要您拥有的键是按字母顺序排列的,它们就会保证按特定顺序排序。
但很多时候,尤其是非常具体的模式,这是行不通的。幸运的是,XML :: Simple仍然支持一种自定义方式:必须使用OO接口,并覆盖sorted_keys方法。这是一个例子:
use strict;
use warnings;
use XML::Simple;
use Data::Dumper;
package MyXMLSimple; # my XML::Simple subclass
use base 'XML::Simple';
# Overriding the method here
sub sorted_keys
{
my ($self, $name, $hashref) = @_;
if ($name eq 'supertag') # only this tag I care about the order;
{
return ('tag1', 'tag3','tag4','tag10'); # so I specify exactly the right order.
}
return $self->SUPER::sorted_keys($name, $hashref); # for the rest, I don't care!
}
package main; # back to main code
my $xmlParser = MyXMLSimple->new( # Create the parser, with options:
KeepRoot => 1, # gives us our root element always.
ForceContent => 1, # ensures that content stays special
);
my $structure = {
supertag => {
tag1 => { content => 'value 1' },
tag10 => { content => 'value 2' },
tag3 => { content => 'value 3' },
tag4 => { content => 'value 4' },
},
};
my $xml = $xmlParser->XMLout($structure);
print "The xml generated is:\n$xml\n";
print "The read in structure then is:\n" . $xmlParser->XMLin($xml) . "\n";
这将给我们:
The xml generated is:
<supertag>
<tag1>value 1</tag1>
<tag3>value 3</tag3>
<tag4>value 4</tag4>
<tag10>value 2</tag10>
</supertag>
The read in structure then is:
$VAR1 = {
'supertag' => {
'tag10' => {
'content' => 'value 2'
},
'tag3' => {
'content' => 'value 3'
},
'tag1' => {
'content' => 'value 1'
},
'tag4' => {
'content' => 'value 4'
}
}
};
查看CPAN上的XML::Simple页面。
答案 1 :(得分:1)
您仍然可以使用`XML :: Simple'并提供进行排序的hook method。我知道,这很难看,你宁愿做一些不会产生额外代码的东西。
答案 2 :(得分:0)
此代码将生成您在评论中要求的输出:
use strict;
use warnings;
use XML::Simple;
my $structure = { 'supertag' => [
'value 1',
'value 2',
'value 3',
'value 4',
],
};
my $xml = XMLout($structure, GroupTags => { supertag => 'tag'});
print "The xml generated is:\n";
print $xml;
print "\n";
它生成:
The xml generated is:
<opt>
<supertag>
<tag>value 1</tag>
<tag>value 2</tag>
<tag>value 3</tag>
<tag>value 4</tag>
</supertag>
</opt>
答案 3 :(得分:0)
对@Konerak发表评论我建议对@ robert-p解决方案进行一些改进。它使得未排序的标签不会消失。
#!/usr/bin/perl -w
use strict;
use warnings;
use XML::Simple;
use Data::Dumper;
package MyXMLSimple; # my XML::Simple subclass
use base 'XML::Simple';
# Overriding the method here
sub sorted_keys
{
my ($self, $name, $hashref) = @_;
if ($name eq 'supertag') # only this tag I care about the order;
{
my @ordered = ('tag1', 'tag3','tag4','tag10');
my %ordered_hash = map {$_ => 1} @ordered;
#set ordered tags in front of others
return @ordered, grep {not $ordered_hash{$_}} $self->SUPER::sorted_keys($name, $hashref);
}
return $self->SUPER::sorted_keys($name, $hashref); # for the rest, I don't care!
}
package main; # back to main code
my $xmlParser = MyXMLSimple->new( # Create the parser, with options:
KeepRoot => 1, # gives us our root element always.
ForceContent => 1, # ensures that content stays special
);
my $structure = {
supertag => {
tag1 => { content => 'value 1' },
tag10 => { content => 'value 2' },
tag3 => { content => 'value 3' },
tag4 => { content => 'value 4' },
tag90 => { content => 'value 90' },
tag91 => { content => 'value 91' },
},
};
my $xml = $xmlParser->XMLout($structure);
my $xml2 = $xmlParser->XMLin($xml);
print "The xml generated is:\n$xml\n";
print "The read in structure then is:\n" . Dumper($xml2) . "\n";
输出
The xml generated is:
<supertag>
<tag1>value 1</tag1>
<tag3>value 3</tag3>
<tag4>value 4</tag4>
<tag10>value 2</tag10>
<tag90>value 90</tag90>
<tag91>value 91</tag91>
</supertag>
The read in structure then is:
$VAR1 = {
'supertag' => {
'tag10' => {
'content' => 'value 2'
},
'tag3' => {
'content' => 'value 3'
},
'tag1' => {
'content' => 'value 1'
},
'tag91' => {
'content' => 'value 91'
},
'tag90' => {
'content' => 'value 90'
},
'tag4' => {
'content' => 'value 4'
}
}
};
答案 4 :(得分:0)
我发现@ mraq解决方案存在问题。我用它并且突然在“supertag”中得到了我不想要的属性,哪些是空的。原因是我没有tag3和tag4所以两者都变成了supertag的空属性。
一个小修补程序可以:
package MyXMLSimple; # my XML::Simple subclass
use base 'XML::Simple';
# Overriding the method here
sub sorted_keys
{
my ($self, $name, $hashref) = @_;
if ($name eq 'dsedib2b') # only this tag I care about the order;
{
my @ordered = qw(
tag1
tag3
tag4
tag10
);
my %ordered_hash = map {$_ => 1} @ordered;
#set ordered tags in front of others
return grep {exists $hashref->{$_}} @ordered, grep {not $ordered_hash{$_}} $self->SUPER::sorted_keys($name, $hashref);
}
return $self->SUPER::sorted_keys($name, $hashref); # for the rest, I don't care!
}
所以只需更换
return @ordered, grep {not $ordered_hash{$_}} $self->SUPER::sorted_keys($name, $hashref);
与
return grep {exists $hashref->{$_}} @ordered, grep {not $ordered_hash{$_}} $self->SUPER::sorted_keys($name, $hashref);
修复了这件事。