XML :: LibXML :: Reader нужно предупреждать об ошибках схемы вместо выхода - PullRequest
3 голосов
/ 18 октября 2019

В основном мне нужно использовать опцию схемы из модуля perl XML :: libXML :: Reader для проверки большого (> 1 ГБ) XML-файла при анализе файла.

Ранее я использовалкоманда xmllint для проверки XML-файла по заданному файлу схемы (xsd). Однако теперь у меня есть несколько больших файлов XML для проверки, и мне не хватает памяти (8 ГБ), чтобы выполнить проверку.

Я прочитал на странице модуля Perl XML :: libXML :: Reader, что естьвариант схемы. Однако когда я его использую (см. Код ниже), код завершается, когда обнаружен первый недействительный элемент 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)
}

Мне нужно собрать недопустимые записи и продолжить, а не выходить. Возможно ли это?

Ответы [ 2 ]

3 голосов
/ 23 октября 2019

Причину $reader->read, которая не восстанавливается после ошибок проверки схемы (даже если восстановление возможно), можно увидеть в строке # 8815 из LibXML.xs. Обратите внимание, что REPORT_ERROR() вызывается с нулевым значением (значение указывает, сможет ли `LibXML_report_error_ctx () восстанавливаться после ошибок или нет. Значение ноль означает, что оно не будет пытаться восстановить, иэто вызовет XML :: LibXML :: Error :: _ report_error , чтобы умереть.

Я попытался изменить значение на 1 в line # 8815 и перекомпилировал XSмодуль, и теперь он сообщил об ошибках схемы в виде предупреждений (вместо того, чтобы умереть) и продолжил анализ.

Я думаю, есть веская причина, почему эта опция не доступна для пользователя, но я не такзнакомый с синтаксическим анализом XML, я могу привести пример того, что здесь может пойти не так.

Редактировать :

Кажется, что правильный подход - перехватывать исключения, выдаваемыеread(), затем попробуйте вызвать read() в другой раз, если следующий вызов read() вернет -1, парсер не сможет восстановиться после ошибки, если он вернет 0, достигнут конец файла,и если он вернет 1, то смогo оправиться от исключения. Я провел некоторое тестирование, и кажется, что оно способно восстановиться после ошибок проверки схемы, но не от ошибок анализа. Таким образом, вы можете попробовать следующее:

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();
1 голос
/ 23 октября 2019

ОК, не совсем то, что я изначально просил, но я нашел решение, если кому-то интересно. Я просто использовал переключатель --stream для команды xmllint.

Это позволяет мне проверять файлы XML> 1 ГБ в системе с 4 ГБ оперативной памяти (без ключа --stream это было бы невозможно). Метод генерирует список записей, если они существуют, которые не соответствуют предоставленному файлу XSD (они могут быть записаны в файл или на терминал). Важным моментом для меня является то, что xmllint не останавливается, когда находит первое несоответствие, а скорее продолжает до конца XML-файла, выводя на печать любые несоответствия.

...