6

关于设置由XMLout. 但是,我无法使用这些答案/示例来解决问题。

我有一个脚本需要输出一些 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_keysopt元素调用时,我只需访问整个哈希引用,但同样无法保证不依赖Tie::模块的输出排序。

现在,我已经设法让它以我想要的方式工作,通过使用Tie::IxHash模块,然后覆盖sorted_keys和(重新)创建一个 subhash 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);
}

尽管这可行(到目前为止似乎工作可靠),但我确实相信必须有一种方法可以在不使用Tie::IxHash模块和进行所有哈希重新排序/重新排序的情况下实现这一点,并且只能通过某种方式从内部排序/返回某些数据sorted_keys

我只是想不通,而且我真的不明白sorted_keys应该如何工作(尤其是当您使用不同/复杂的输入数据集得到不同的结果时;),但我希望有人知道这一点.

我的意思是,我已经尝试XML/Simple.pm在子程序的最后一个返回行中修改自身并更改排序顺序sorted_keys,但我仍然得到按字母数字排序的输出。恐怕我无法弄清楚如何修改它,因此它不会排序,name而是排序i

4

2 回答 2

2

我相信此时您已经超越了 XML::Simple。如果您关心元素中子项的顺序,那么是时候使用更类似于 XML 的模块了。对于您想要的 XML 创建样式,可能是XML::TreeBuilder,请查看new_from_lol方法。或者 XML::LibXML、XML::Twig、XML::Writer……

我过去也尝试过混合 Tie::IxHash 和 XML::Simple,但效果并不好。你实际上已经很远了。但我相信这种方式是疯狂的

于 2010-11-12T14:41:12.127 回答
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 => { ...

当然,这很丑陋,哨兵需要将 'i' 设置为 KeyAttr 并且为时已晚 5 年,但它有效,现在我可以去做其他事情了 :)

于 2016-06-07T08:01:36.983 回答