【问题标题】:Best way to parse complicated XML with Perl?用 Perl 解析复杂 XML 的最佳方法?
【发布时间】:2014-09-02 06:55:51
【问题描述】:

我必须使用 Perl 解析几个 XML 文件并将变量存储在哈希中。如果可能的话,我想过滤某些属性。稍后在我的代码中,我从哈希中提取数据并插入到数据库中。

我一直在使用XML::Parser,但我更愿意解析成哈希而不是处理遇到的每个标签。有什么建议吗?

我想跳过任何具有kind="dir" 属性的路径。我需要路径的作者、日期、消息和文件类型(文件扩展名)。 <path> 标签可以有任意数量,可以是kind“文件”或“目录”。也可以有多个<logentry>标签。

<?xml version="1.0" encoding="UTF-8"?>
<log>
    <logentry revision="3989">
        <author>cergyl</author>
        <date>2013-07-19T05:31:01.212620Z</date>
        <paths>
            <path action="M" kind="dir">/team.admin/trunk/auth.conf</path>
        </paths>
        <path action="M" kind="file">/team.admin/trunk/file.cpp</path>
        <msg>Whitespace change to verify repository synchronization</msg>
    </logentry>
</log>

my $XML_Parser = XML::Parser->new(
                                  Handlers => {
                                                 Start   => \&hdl_xml_tag_start,
                                                 End     => \&hdl_xml_tag_end,
                                                 Char    => \&hdl_xml_nonmarkup_char,
                                                 Default => \&hdl_xml_default
                                               }
                                 );

# This event is generated when an XML start tag is recognized. Parser is an XML::Parser::Expat instance.
sub hdl_xml_tag_start
{
    my ( $parser, $element, %attributes ) = @_;
    $attributes{ '_str' } = "$element:";
    $XML_Attributes_Hash_Ref = \%attributes;
    return;
}

# This event is generated when an XML end tag is recognized. Note that an XML empty tag (<foo/>) generates both a start and an end event.
sub hdl_xml_tag_end
{
    my ( $parser, $element ) = @_;

    #format_message($XML_Attributes_Hash_Ref);
    format_svn_history( $XML_Attributes_Hash_Ref );
    return;
}


# This event is generated when non-markup is recognized. The non-markup sequence of characters is in String.
# A single non-markup sequence of characters may generate multiple calls to this handler.
sub hdl_xml_nonmarkup_char
{
    my ( $parser, $string ) = @_;
    $XML_Attributes_Hash_Ref->{ '_str' } .= $string;
    return;
}

#This is called for any characters that don't have a registered handler.
sub hdl_xml_default { return; }

【问题讨论】:

  • 为什么XML::Parser 不适合你?
  • 我真的很喜欢 XML::Twig,尤其是因为它让我可以purge 来节省内存占用。
  • XML::Parser 是一种低级解析。我同意你摆脱它的愿望。但是,我建议不要使用产生散列的解析器。它们是最难使用的。我使用 XML::LibXML(非常快,非常强大,支持任何 XML),但 XML::Twig 也很流行(尤其是当您的 XML 文件是一长串记录时)。
  • 您的 XML 结构相当混乱。单个logentry 不足以了解其他条目的外观。您想完全忽略所有paths 元素,只使用msg 之前的path?这些path 元素中是否总是只有一个?如果没有更多信息,很难为您提供帮助
  • 如果您需要帮助,那么您必须更好地解释自己。 “将变量存储在哈希中”“如果可能,过滤某些属性” 根本没有帮助。请展示您希望查看的结果哈希示例,以及“通过某些属性过滤” 的含义。请不要仅仅因为您已经看到或听说过将 XML 处理为哈希的模块而要求哈希。这样做很明显是一个雷区,应该避免。选择最适合您在读取数据后对数据执行的操作的数据表示形式。

标签: perl xml-parsing


【解决方案1】:

就我个人而言,我喜欢来自XML::DOM 的 XML::DOM::Parser。但我使用 XML::Twig 来漂亮地打印它们。

my $xp = XML::DOM::Parser->new(); my $doc = $xp->parse("<xml></xml>"); $doc->dispose(); my $doc = $xp->parsefile("file.xml"); $doc->dispose(); // Pretty Print My poorly formatted xml doc my $xpp = XML::Twig->new(pretty_print => 'indented'); $xpp->parse("<xml></xml>"); $xpp->print();

【讨论】:

    【解决方案2】:

    由于您提供的信息有限,很难编写一个全面的解决方案,但这里有一些使用XML::Twig 来处理您显示的 XML 数据并显示所有(一个)path 元素的东西有一个kind 属性等于dir

    XML::LibXML也是基于C编码的libxml2的一个非常高质量的模块

    use strict;
    use warnings;
    
    use XML::Twig;
    
    my $parser = XML::Twig->new(
      twig_handlers => {
        path => \&path_handler,
      }
    );
    
    $parser->parse(*DATA);
    
    sub path_handler {
      my ($twig, $path) = @_;
      return if $path->att('kind') eq 'dir';
      print $path->text, "\n";
    }
    
    
    __DATA__
    <?xml version="1.0" encoding="UTF-8"?>
    <log>
        <logentry revision="3989">
            <author>cergyl</author>
            <date>2013-07-19T05:31:01.212620Z</date>
            <paths>
                <path action="M" kind="dir">/team.admin/trunk/auth.conf</path>
            </paths>
            <path action="M" kind="file">/team.admin/trunk/file.cpp</path>
            <msg>Whitespace change to verify repository synchronization</msg>
        </logentry>
    </log>
    

    输出

    /team.admin/trunk/file.cpp
    

    【讨论】:

      猜你喜欢
      • 2018-01-17
      • 1970-01-01
      • 1970-01-01
      • 2015-11-03
      • 2011-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-21
      相关资源
      最近更新 更多