【问题标题】:Perl data structure to xmlPerl 数据结构到 xml
【发布时间】:2015-03-17 01:29:54
【问题描述】:

我有类似下面的 perl 数据:

$data = {
   id => 1,
   name => "A",
   users  => [ { id => 1, name => "u1" }, { id => 2, name => "u2" } ],
   groups => [ { id => 1, name => "g1" } ]
};

我想把它转换成如下的xml:

<map>
  <item id="1" name="A">
     <users>
        <user id="1" name="u1"/>
        <user id="2" name="u2"/> 
     </users>
     <groups>
        <group id="1" name="g1"/>
     </groups>         
  </item>
</map>

我可以手动显式地创建每一行。不过,我正在寻找任何 CPAN 模块基础解决方案。

我尝试了 XML::Twig,但没有去任何地方。我过去曾用过 XML::Simple 来做这样的事情,但这次想尝试其他东西,因为 XML::Simple 一直受到不好的评价。

【问题讨论】:

  • 你最初是如何创建这个数据结构的?它远非创建 XML 数据的理想之选,而且看起来像 XML::Simple 将创建的东西。例如,无法知道groupsusers 中元素的名称。而mapitem 这两个名字是从哪里来的?
  • 我想你会从解析 JSON 中得到类似的东西——JSON 中有数组,而 XML 没有。
  • @Sobrique:但是XML::Simple 创造了这样的东西:)
  • 另一个不喜欢XML::Simple的理由:)

标签: perl


【解决方案1】:

您可以像 Sobrique 的方法一样执行此操作,但使用较少的硬编码字符串,如下所示:

#!/usr/bin/env perl
use strict; use warnings;

use XML::Twig;

my $data = {
    id => 1,
    name => "A",
    users  => [ { id => 1, name => "u1" }, { id => 2, name => "u2" } ],
    groups => [ { id => 1, name => "g1" } ]
};

sub array_to_elts {
    my ( $root, $name, $arrayref ) = @_;
    map { $root->insert_new_elt($name, $_) } @{ $arrayref };
}

my $twig  = XML::Twig
    ->new()
    ->set_xml_version("1.0")
    ->set_encoding('utf-8');

my $map = XML::Twig::Elt->new('map');
$twig->set_root($map);

my $item  = $map->insert_new_elt(
    'item',
    { id => $data->{'id'}, name => $data->{'name'} },
);

my $lines = $item->insert_new_elt('groups');
my $links = $item->insert_new_elt('users' );

array_to_elts($lines, 'group', $data->{'groups'});
array_to_elts($links, 'user',  $data->{'users' });

$twig->set_pretty_print('indented');
$twig->print;

您可以不遗余力地减少硬编码的 val 并更多地基于原始数据,但它很快就会变得难以阅读。..

【讨论】:

  • 感谢您的解决方案,David,一如既往。
  • 在数组上应用insert_new_elt是否会恢复顺序,即:insert_new_elt在最后一个元素之前插入新元素?
【解决方案2】:

使用XML::LibXML 的“通用”方式。您可能需要在“else”部分添加新代码来处理其他类型的结构。

#!/usr/bin/perl
use warnings;
use strict;

use XML::LibXML;

my $data = {
            id     => 1,
            name   => "A",
            users  => [ { id => 1, name => "u1" },
                        { id => 2, name => "u2" } ],
            groups => [ { id => 1, name => "g1" } ],
           };

sub to_xml {
    my ($data, $xml) = @_;
    for my $entry (keys %$data) {
        my $ref = ref $data->{$entry};
        if (not $ref) {
            $xml->setAttribute($entry, $data->{$entry});

        } elsif ('ARRAY' eq $ref) {
            (my $name = $entry) =~ s/s$// or die "Can't guess the element name.\n";
            my $list = $xml->addNewChild(q(), $entry);
            for my $inner (@{ $data->{$entry} }) {
                to_xml($inner, $list->addNewChild(q(), $name));
            }

        } else {
            die "Unhandled structure $ref.\n";
        }
    }
}

my $xml = 'XML::LibXML::Document'->createDocument;
my $root = $xml->createElement('map');
$xml->setDocumentElement($root);
for my $entry ($data) {
    my $item = $root->addNewChild(q(), 'item');
    to_xml($entry, $item);
}

print $xml;

【讨论】:

  • 感谢您的解决方案,Choroba。我必须说这看起来很硬核。
  • @MohammadSajidAnwar:XML 是硬核 :-)
  • 也许编辑您的答案以包括记录 addNewChild 的位置;我在 XML::LibXML 和 XML::LibXML::Document 中都找不到它。
  • addNewChild 记录在 XML::LibXML::Node 中。
【解决方案3】:

是的,明智的选择。 XML::Simple ... 不是。它是 for 简单的 XML。

正如 cmets 中所指出的 - 您的数据有点模棱两可 - 具体来说,您如何判断在“组”或“用户”中应该调用哪些元素。

看起来您可能已经解析了一些 JSON。 (确实可以直接转回JSON:

print to_json ( $data, { pretty => 1 } );

核心问题是 - JSON 支持数组,而 XML 不支持。因此,您几乎无法直接将您的数据结构转换为 XML。

但是,如果您不介意自己做一些工作:

以下是使用 XML::Twig 组装一些 XML 的方法

Assembling XML in Perl

use strict;
use warnings;

use XML::Twig;

my $twig = XML::Twig->new( 'pretty_print' => 'indented' );
$twig->set_root( 
    XML::Twig::Elt->new(
        'map',
    )
);
my $item = $twig->root->insert_new_elt('item', { 'id' => 1, 'name' => 'A' } );
my $users = $item ->insert_new_elt( 'users' );
   $users -> insert_new_elt ( 'user', { 'id' => 1, 'name' => 'u1' } );
   $users -> insert_new_elt ( 'user', { 'id' => 2, 'name' => 'u2' } );

my $groups = $item -> insert_new_elt ('last_child', 'groups');
   $groups -> insert_new_elt ( 'group', { 'id' => 1, 'name' => 'g1' } );

$twig->set_xml_version("1.0");
$twig->set_encoding('utf-8');

$twig->print;

哪些打印:

<?xml version="1.0" encoding="utf-8"?>
<map>
  <item id="1" name="A">
    <users>
      <user id="2" name="u2"/>
      <user id="1" name="u1"/>
    </users>
    <groups>
      <group id="1" name="g1"/>
    </groups>
  </item>
</map>

迭代您的数据结构留给读者作为练习。

正如 Borodin 正确指出的那样 - 您无法从数据结构中推断出 map item groupuser。后两者你可以也许根据复数推断,但鉴于你的数据集,我能想到的最好的结果是这样的:

use strict;
use warnings;

use XML::Twig;

my $data = {
    id    => 1,
    name  => "A",
    users => [ { id => 1, name => "u1" }, { id => 2, name => "u2" } ],
    groups => [ { id => 1, name => "g1" } ]
};


my $twig = XML::Twig->new( 'pretty_print' => 'indented' );
$twig->set_root( XML::Twig::Elt->new( 'map', ) );

my $item = $twig->root->insert_new_elt('item');
foreach my $key ( keys %$data ) {
    if ( not ref $data->{$key} ) {
        $item->set_att( $key, $data->{$key} );
        next;
    }
    if ( ref( $data->{$key} ) eq "ARRAY" ) {
        my $fakearray = $item->insert_new_elt($key);
        foreach my $element ( @{ $data->{$key} } ) {
            my $name = $key;
               $name =~ s/s$//g;
            $fakearray->insert_new_elt( $name, $element );
        }
        next;
    }
    if ( ref ( $data -> {$key} ) eq "HASH" ) { 
        $item -> insert_new_elt( $key, $data -> {$key} );
        next;
    }
}

$twig->set_xml_version("1.0");
$twig->set_encoding('utf-8');

$twig->print;

这并不理想,因为 - map 是硬编码的,item 也是如此。我采用非常简单的方法,假设数组末尾有一个s,将其复数化。

【讨论】:

  • 这将创建正确的 XML,当然,但我认为 OP 正在寻找一种将 Perl 数据结构转换为 XML 的 generic 方法。您也可以只输入 XML 而不是输入代码来创建它。
  • 我认为它是一个相当大的数据结构,需要对其进行迭代以生成一些 XML,而不是手动输入。如果它只是简单地...从 JSON(也许?)到 XML 的东西,那就更复杂了。
  • 那我们同意。但是您的代码没有使用 OP 的数据结构。正如我上面评论的那样,没有办法获得标签名称mapitemusergroup
  • 嗯,是的。你必须做出某些假设。 'user' 和 'group' 你可能会因为复数而捏造,但是......
  • 感谢 Sobrique 的解决方案,非常感谢。
猜你喜欢
  • 2011-02-23
  • 2014-04-27
  • 2012-02-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-22
  • 2011-02-24
  • 1970-01-01
  • 2020-06-02
相关资源
最近更新 更多