Perl数据结构为xml

时间:2015-03-16 16:28:55

标签: perl

我的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已经得到了不好的评论。

3 个答案:

答案 0 :(得分: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,并从原始数据中获得更多的东西,但它很快就会变得难以阅读..

答案 1 :(得分:1)

使用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;

答案 2 :(得分:0)

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

正如评论中所述 - 您的数据有点含糊不清 - 具体来说,您如何判断应该在&#39;组内调用哪些元素?或者&#39;用户&#39;。

这看起来你可能已经解析了一些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,以便将其复数化。