关于设置XMLout
返回的XML元素的顺序,我已经在各个地方看到了很少的答案。但是,我无法使用这些答案/示例来解决问题。
我有一个需要输出一些XML数据的脚本,某些元素需要按特定顺序打印。哈希非常复杂,我无法通过覆盖sorted_keys
对象中的XML::Simple
来获得任何结果。嗯,我做了,但不是我想要的方式。
示例代码如下,有关问题的详细信息在代码下方。
#!/usr/bin/perl
use strict;
use warnings;
use XML::Simple;
package MyXMLSimple;
use base 'XML::Simple';
sub sorted_keys
{
my ($self, $name, $hashref) = @_;
# ...
return $self->SUPER::sorted_keys($name, $hashref);
}
package main;
my $xmlParser = MyXMLSimple->new;
my $items = {
'status' => 'OK',
'fields' => {
'i1' => {
'header' => 'Header 1',
'max_size' => '3'
},
'i2' => {
'header' => 'Header 2',
'max_size' => '8'
}
},
'item_list' => {
'GGG' => {
'index' => '3',
'i' => 3,
'points' => {
'p5' => {
'data' => '10',
}
},
},
'AAA' => {
'index' => '1',
'i' => 2,
'points' => {
'p7' => {
'data' => '22',
}
},
},
'ZZZ' => {
'index' => '2',
'i' => 1,
'points' => {
'p6' => {
'data' => '15',
}
},
}
}
};
my $xml = $xmlParser->XMLout($items);
print "$xml";
因此,此脚本的输出将为:
<opt status="OK">
<fields name="i1" header="Header 1" max_size="3" />
<fields name="i2" header="Header 2" max_size="8" />
<item_list name="AAA" i="2" index="1">
<points name="p7" data="22" />
</item_list>
<item_list name="GGG" i="3" index="3">
<points name="p5" data="10" />
</item_list>
<item_list name="ZZZ" i="1" index="2">
<points name="p6" data="15" />
</item_list>
</opt>
通过对item_list
属性进行排序,打印出 name
个元素,输出顺序按字母顺序排列。输出顺序为AAA,GGG,ZZZ。
但是,我需要的是在i
元素上进行排序(数字,从最低到最高)时输出。因此输出将按顺序ZZZ,AAA,GGG。
我无法控制哈希中的顺序(不是没有使用Tie::...
模块),所以我不能这样做。如果我使用NoSort => 1
,输出将不会被任何特定的排序,所以我最终会得到随机输出。
所以,我很确定必须有一种方法可以通过覆盖sorted_keys
子例程来按照我想要的方式对其进行排序。但是,我无法获得我想要的结果,因为sorted_keys
会为每个item_list
实例调用sorted_keys
。当为opt
元素调用Tie::
时,我只能访问整个哈希引用,但是再次无法保证输出顺序而不依赖于Tie::IxHash
模块。
现在,我已经设法通过使用sorted_keys
模块,然后通过重新插入值来覆盖item_list
和(重新)创建子哈希sub sorted_keys
{
my ($self, $name, $hashref) = @_;
if ($name eq "opt")
{
my $clist = { };
tie %{$clist}, "Tie::IxHash";
my @sorted_keys = sort { $hashref->{item_list}->{$a}->{i} <=> $hashref->{item_list}->{$b}->{i} } keys %{$hashref->{item_list}};
foreach my $sorted_key (@sorted_keys)
{
$clist->{$sorted_key} = $hashref->{item_list}->{$sorted_key};
}
delete $hashref->{item_list};
$hashref->{item_list} = $clist;
}
return $self->SUPER::sorted_keys($name, $hashref);
}
,以此方式工作。从原始哈希到新的(有序的)哈希,然后删除原始哈希中的subhash,并用新的有序哈希替换它。
这样的事情:
Tie::IxHash
虽然这有效(并且到目前为止似乎可靠地工作),但我确实认为必须有一种方法可以实现这一点,而无需使用sorted_keys
模块并执行所有哈希重新排序/重新排序,并且只能通过某种方式排序/从sorted_keys
内返回某些数据。
我无法理解,我真的不明白XML/Simple.pm
应该如何工作(尤其是当您使用不同/复杂的输入数据集获得不同的结果时);但我希望那里有人知道这一点。
我的意思是,我已经尝试修改sorted_keys
本身并在name
子例程的最后一个返回行中更改排序顺序,但我仍然得到字母数字排序的输出。我恐怕无法弄清楚如何修改它,因此不会对i
进行排序,而是对{{1}}进行排序。
答案 0 :(得分:2)
我相信在这一点上你已经超越了XML :: Simple。如果你关心元素中子元素的顺序,那么就该使用更多XML-ish模块了。对于您想要的XML创建风格,可能XML::TreeBuilder,请查看new_from_lol
方法。或XML :: LibXML,XML :: Twig,XML :: Writer ......
我过去也尝试过混合使用Tie :: IxHash和XML :: Simple,但它并不漂亮。你真的在这里相当远。但我相信这种方式是疯狂的
答案 1 :(得分:0)
也许看看重写hash_to_array?为我工作了。 http://perlmaven.com/xml-simple-sorting
我想对标记键执行自然排序,并通过重写XML :: Simple hash_to_array来实现。我基本上从XML :: Simple.pm复制了这个方法并对自然排序做了一个小编辑 - 就像这样:
package MyXMLSimple; # my XML::Simple subclass
use base 'XML::Simple';
sub hash_to_array {
my $self = shift;
my $parent = shift;
my $hashref = shift;
my $arrayref = [];
my($key, $value);
if ( $parent eq "mytag" ) {
my @keys = $self->{opt}->{nosort} ? keys %$hashref : sort {$a<=>$b} keys %$hashref;
foreach $key (@keys) {
$value = $hashref->{$key};
return($hashref) unless(UNIVERSAL::isa($value, 'HASH'));
if(ref($self->{opt}->{keyattr}) eq 'HASH') {
return($hashref) unless(defined($self->{opt}->{keyattr}->{$parent}));
push @$arrayref, $self->copy_hash(
$value, $self->{opt}->{keyattr}->{$parent}->[0] => $key
);
}
else {
push(@$arrayref, { $self->{opt}->{keyattr}->[0] => $key, %$value });
}
}
} else {
my @keys = $self->{opt}->{nosort} ? keys %$hashref : sort keys %$hashref;
foreach $key (@keys) {
$value = $hashref->{$key};
return($hashref) unless(UNIVERSAL::isa($value, 'HASH'));
if(ref($self->{opt}->{keyattr}) eq 'HASH') {
return($hashref) unless(defined($self->{opt}->{keyattr}->{$parent}));
push @$arrayref, $self->copy_hash(
$value, $self->{opt}->{keyattr}->{$parent}->[0] => $key
);
}
else {
push(@$arrayref, { $self->{opt}->{keyattr}->[0] => $key, %$value });
}
}
}
return($arrayref);
}
my $xmlParser = MyXMLSimple->new(KeepRoot => 1);
$xmlParser->XMLout($self->{_xml}, KeyAttr => { ...
当然,它的丑陋,哨兵需要设置“我”。作为KeyAttr和它的5年已经太晚但是它起作用了,现在我可以去做别的事了:))