【问题标题】:Parsing XML file in Perl - Retain sequence在 Perl 中解析 XML 文件 - 保留序列
【发布时间】:2015-09-28 23:47:50
【问题描述】:

XML结构如下:

<Entities>
    <Entity>
        <EntityName>.... </EntityName>
        <EntityType>.... </EntityType>
        <Tables>
            <DataTables>
                <DataTable>1</DataTable>
                <DataTable>2</DataTable>
                <DataTable>3</DataTable>
                <DataTable>4</DataTable>
            </DataTables>
            <OtherTables>
                <OtherTable>5</OtherTable>
                <OtherTable>6</OtherTable>
            </OtherTables>
        </Tables>
    </Entity>
.
.
.
</Entities>

我需要根据选择的实体名称解析文件,并按提到的顺序专门检索所有表。如何在 Perl 中执行此操作以及应该使用哪个模块?

【问题讨论】:

  • 您知道,XML 不是这样设置的。如果你想要真正的元素排序,最好有一个名为“order”的属性填充顺序值。

标签: xml perl


【解决方案1】:

我最喜欢在 Perl 中解析 XML 的模块是 XML::Twig (tutorial)。

代码示例:

use XML::Twig;

my $twig = XML::Twig->new(
    twig_handlers => {
        #calls the get_tables method for each Entity element
        Entity    => sub {get_tables($_);},
    },
    pretty_print  => 'indented',                # output will be nicely formatted
    empty_tags    => 'html',                    # outputs <empty_tag />
    keep_encoding => 1,
);

$twig->parsefile(xml-file);
$twig->flush;

sub get_tables {
    my $entity = shift;

    #Retrieves the sub-elements of DataTables
    my @data_tables = $entity->first_child("Tables")->children("DataTables");
    #Do stuff with the DataTables

    #Retrieves the sub-elements of OtherTables
    my @other_tables = $entity->first_child("Tables")->children("OtherTables");
    #Do stuff with the OtherTables

    #Flushes the XML element from memory
    $entity->purge;
}

【讨论】:

  • 此外,任何元素的子元素列表都将按文档顺序排列,即。与xml文件中的相同。
【解决方案2】:

文档顺序defined

有一个排序,文档顺序,定义在文档的所有节点上,对应于每个节点的XML表示的第一个字符在文档的XML表示中出现的顺序一般实体扩充后。因此,根节点将是第一个节点。元素节点出现在它们的子节点之前。因此,文档顺序按元素节点在 XML 中出现的顺序对元素节点进行排序(在实体展开之后)。

换句话说,就是事物在 XML 文档中发生的顺序。 XML::XPath 模块按文档顺序生成结果。例如:

#! /usr/bin/perl

use warnings;
use strict;

use XML::XPath;

my $entity_template = "/Entities"
                    . "/Entity"
                    .   "[EntityName='!!NAME!!']"
                    ;

my $tables_path = join "|" =>
                  qw( ./Tables/DataTables/DataTable
                      ./Tables/OtherTables/OtherTable );

my $xp = XML::XPath->new(ioref => *DATA);

foreach my $ename (qw/ foo bar /) {
  print "$ename:\n";
  (my $path = $entity_template) =~ s/!!NAME!!/$ename/g;
  foreach my $n ($xp->findnodes($path)) {
    foreach my $t ($xp->findnodes($tables_path, $n)) {
      print $t->toString, "\n";
    }
  }
}

__DATA__

第一个表达式搜索&lt;Entity&gt; 元素,其中每个元素都有一个&lt;ElementName&gt; 子元素,其string-value 是选定的实体名称。从那里,我们寻找&lt;DataTable&gt;&lt;OtherTable&gt;

给定输入

<Entities>
    <Entity>
        <EntityName>foo</EntityName>
        <EntityType>type1</EntityType>
        <Tables>
            <DataTables>
                <DataTable>1</DataTable>
                <DataTable>2</DataTable>
            </DataTables>
            <OtherTables>
                <OtherTable>3</OtherTable>
                <OtherTable>4</OtherTable>
            </OtherTables>
        </Tables>
    </Entity>
    <Entity>
        <EntityName>bar</EntityName>
        <EntityType>type2</EntityType>
        <Tables>
            <DataTables>
                <DataTable>5</DataTable>
                <DataTable>6</DataTable>
            </DataTables>
            <OtherTables>
                <OtherTable>7</OtherTable>
                <OtherTable>8</OtherTable>
            </OtherTables>
        </Tables>
    </Entity>
</Entities>

输出是

foo:
<DataTable>1</DataTable>
<DataTable>2</DataTable>
<OtherTable>3</OtherTable>
<OtherTable>4</OtherTable>
bar:
<DataTable>5</DataTable>
<DataTable>6</DataTable>
<OtherTable>7</OtherTable>
<OtherTable>8</OtherTable>

要提取字符串值(“内部文本”),请将$tables_path 更改为

my $tables_path = ". / Tables / DataTables  / DataTable  / text() |
                   . / Tables / OtherTables / OtherTable / text()";

是的,这是重复的——因为 XML::XPath 实现了XPath 1.0

输出:

富:
1
2
3
4
酒吧:
5
6
7
8

【讨论】:

  • 嗨.. 我怎样才能只使用 XPath 获取值?例如:1 2 3 4
【解决方案3】:

我更喜欢XML::LibXML,它允许您(和我)使用 XPath 来选择元素。

您不妨查看script I wrote with it

【讨论】:

    【解决方案4】:

    xml-simple

    在使用它之前,请记住一些要点,例如

    XML::Simple 能够呈现一个简单的 API,因为它代表您做出了一些假设。其中包括:

    • 您对文本内容不感兴趣 仅由空格组成
    • 你不介意当事情发生时 啜饮到散列中,订单丢失
    • 您不想要细粒度的控制 生成的 XML 的格式
    • 你永远不会使用散列键 不是合法的 XML 元素名称
    • 您不需要帮助转换 不同编码之间

    对于基于事件的解析,使用 SAX(不要着手为 XML::Parser 的处理程序 API 编写任何新代码 - 它已过时)。

    对于基于树的解析,您可以在 XML::Twig 的“Perlish”方法和更多基于标准的 DOM 实现之间进行选择 - 最好是支持 XPath 的一种。

    来源:XML-Simple

    有关 Perl-XML 的更多详细信息,请参阅Perl-XML

    【讨论】:

    • 谢谢,但我尝试过 XML-simple。参考资料说“元素的顺序不同,因为哈希不保留它们包含的项目的顺序”。所以我怀疑表格的顺序是否会保持..
    • @Abhi:是的,XML-Simple 假设当事物在哈希中被吞食时,顺序就会丢失。
    • 这个答案不好:使用 XML::Simple 的建议。非常好:解释为什么不好。
    猜你喜欢
    • 2013-07-17
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 2017-10-28
    • 1970-01-01
    • 2012-03-27
    相关资源
    最近更新 更多