【问题标题】:XML::LibXML::Reader need warn on schema errors instead of exitXML::LibXML::Reader 需要警告架构错误而不是退出
【发布时间】:2020-02-15 07:38:03
【问题描述】:

基本上,我需要使用 perl 模块 XML::libXML::Reader 中的 schema 选项,以便在解析文件时验证大型 (>1GB) XML 文件。

之前我使用 xmllint 命令根据给定的模式 (xsd) 文件验证 XML 文件。但是,现在我有一些大型 XML 文件要验证,并且在尝试执行验证时内存不足 (8GB)。

我在 XML::libXML::Reader perl 模块页面上读到有一个架构选项。但是,当我使用它时(参见下面的代码),当找到 XML 文件的第一个无效元素时,代码就会退出。

use strict;
use warnings;
use XML::LibXML::Reader;

my $SchemaFile='schema.xsd';
my $FileToAnalyse='/tmp/file.xml';

my $reader = XML::LibXML::Reader->new(location => $FileToAnalyse,Schema=>$SchemaFile) or 
die "cannot read file '$FileToAnalyse': $!\n";

while($reader->read) {

    Process the file line by line here, even if not valid against schema (reduces memory usage for large files)
}

我需要收集无效条目并继续而不是退出。这可能吗?

【问题讨论】:

  • 吞下 XML::LibXML::Error 异常似乎会使 $reader 进入无效状态。 spec 表示解析器可以继续。
  • 试试这个教程culturedperl.com/…

标签: xml linux perl xml-libxml


【解决方案1】:

$reader->read 无法从架构验证错误中恢复(即使可以恢复)的原因可以在LibXML.xsline #8815 中看到。请注意,REPORT_ERROR() 以零值调用(该值指示 `LibXML_report_error_ctx() 是否能够从错误中恢复。零值表示它不会尝试恢复,它将调用 XML::LibXML::Error::_report_error 以死。

我尝试将 line #8815 的值更改为 1 并重新编译 XS 模块,现在它将架构错误报告为警告(而不是死亡)并继续解析。

我想这个选项不提供给用户是有充分理由的,但我对 XML 解析不太熟悉,所以我可以在这里举一个可能出错的例子。

编辑

看来正确的做法是捕获read()抛出的异常,然后尝试再次调用read(),如果下面对read()的调用返回-1,则解析器无法从中恢复错误,如果返回 0,则到达文件末尾,如果返回 1,则能够从异常中恢复。我做了一些测试,它似乎能够从模式验证错误中恢复,但不能从解析错误中恢复。因此,您可以尝试以下方法:

use feature qw(say);
use strict;
use warnings;

use Try::Tiny qw(try catch);
use XML::LibXML::Reader;

my $SchemaFile='schema.xsd';
my $FileToAnalyse='file.xml';
my $reader = XML::LibXML::Reader->new(
    location => $FileToAnalyse, Schema => $SchemaFile
) or die "cannot read file '$FileToAnalyse': $!\n";
while (1) {
    my $result;
    try { $result = $reader->read } catch {
        say '==> ' . $_;
        $result = 1;  # Try to continue after exception..
    };
    last if $result != 1;
    if ( $reader->nodeType == XML_READER_ELEMENT ) {
        say "Element node: ", $reader->name;
    }
}
$reader->finish();
$reader->close();

【讨论】:

    【解决方案2】:

    好的,不完全是我最初的要求,但如果有人感兴趣,我已经找到了解决方案。我只是为 xmllint 命令使用了 --stream 开关。

    这允许我在具有 4GB 内存的系统上验证 >1GB 的 XML 文件(如果没有 --stream 开关,这是不可能的)。该方法生成一个条目列表,如果它们存在,则不符合提供的 XSD 文件(这些可以写入文件或终端)。对我来说重要的一点是 xmllint 在发现第一个不符合项时不会停止,而是继续到 XML 文件的末尾打印任何不符合项。

    【讨论】:

    • 我与一个正在实现验证 XML 解析器的开发团队合作。当我要求提供“仅警告”功能时,他们解释说,除非在特定情况下,否则一般不可能提供此功能。一旦脱离了模式的“语法”,通常就不清楚 XML 处理器应该如何继续。例外是在属性值上验证 XSD 构面 - 这些错误总是可以生存的,因为它们只涉及单个属性的值。
    猜你喜欢
    • 2018-12-23
    • 2011-02-11
    • 2017-11-29
    • 2021-10-18
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 2010-12-15
    相关资源
    最近更新 更多