如何在XML :: Simple的输出中订购标签?

时间:2009-09-09 16:58:28

标签: xml perl

这是我的情景:

我需要通过Perl生成XML,其中架构中充满了<xs:sequence>个标签(即标签必须按顺序出现)。我无法控制架构(第三方),每当我们添加新的CPAN模块(没有很好的方式将它们传播给客户等)时,我们已经基本上禁止这些模块出现了很多问题。添加任何新内容(例如XML::Writer)。

我可以使用的XML模块包括:XML::ParserXML::SimpleXML::XPath

我非常喜欢在XML::Simple中创建hashref w / hash / arary refs数据结构然后只是吐出XML的方式。

无论如何都要使用XML::Simple执行此操作?或者也许滚动我自己的代码按顺序吐出XML?似乎我最大的问题是我需要以插入顺序从hashref中提取内容,而Perl并没有真正做到这一切。我确实读过关于Tie::IxHash的问题,因为它是按照插入顺序提取的,但同样是我没有的模块。

感觉我有点像SOL,但肯定会感谢有人可能拥有的任何技巧/想法。感谢。

5 个答案:

答案 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);

修复了这件事。