Этот пост представляет собой краткую, но практическую демонстрацию проверки XML-документа на соответствие файлам Document Type Definition (DTD) и XML Schema Definition (XSD). Такая проверка необходима, чтобы гарантировать, что XML, отправленный между клиентом и сервером, на котором размещена веб-служба на основе XML, получен должным образом. И DTD, и XSD позволяют определять элементы и атрибуты, которые должен содержать XML-документ. XSD имеет то преимущество, что он сам написан на XML, что упрощает чтение. XSD также позволяет определять больше информации об элементе, например их тип данных, пространство имен и ограничения для значений. В Perl 5 есть несколько модулей для проверки XML, но я буду использовать два из пространства имен Lib :: XML.

Настройка проекта

Файлы проекта

Я буду использовать следующие файлы, содержащиеся в каталоге с именем perl5-xml-validation, для демонстрации проверки DTD и XSD.

perl5-xml-validation/
.
├── bin
│   ├── dtd_validation.pl
│   └── xsd_validation.pl
├── cpanfile
└── xml
    ├── book.xml
    └── schemas
        ├── book.dtd
        └── book.xsd

Создав каталог, обязательно войдите в него с помощью cd perl5-xml-validation.

Зависимости

Откройте файл cpanfile и перечислите следующие модули:

requires 'XML::LibXML';
requires 'Try::Tiny';

Запустите cpanm -L local --installdeps ., чтобы установить модули. Я объясню другие файлы, когда мы начнем их использовать.

XML

XML-документ, используемый для этой демонстрации, просто содержит сведения о книге в файле ./xml/book.xml следующим образом:

<?xml version="1.0" encoding="utf-8" ?>
<book>
    <title>XML Validation</title>
    <numPages>-200</numPages>
</book>

Значение «-200» для элемента numPages установлено намеренно. Причина станет ясна позже.

Проверка XML с помощью DTD

Файл DTD

Файл ./xml/schemas/book.dtd содержит следующие определения:

<!ELEMENT book (title,author,numPages?)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT numPages (#PCDATA)>

Чтобы любой XML-документ, связанный с этим DTD, был действительным, он должен содержать элементы book, title, author и, возможно, numPages . Как вы заметили, элемент author отсутствует в XML, который мы определили ранее - мы исправим это позже.

Теперь давайте проверим XML. Файл ./bin/dtd_validation.pl содержит следующий код, который я объясню ниже.

Код

#!/usr/bin/env perl
use v5.18;
use warnings;

use XML::LibXML;
use Try::Tiny qw(try catch);
my $xml_doc = XML::LibXML->load_xml(location => './xml/book.xml');
my $dtd_doc = XML::LibXML::Dtd->new('', './xml/schemas/book.dtd');
my $is_xml_valid = try {
    $xml_doc->validate($dtd_doc)
}
catch {
    say '==> ' . $_;
    return 0;
};
say $is_xml_valid ? 'Valid' : 'Invalid';

Что касается проверки, приведенный выше код делает следующее:

  • Загружает XML-файл с использованием load_xml, который возвращает объект XML::LibXML::Document, назначенный $xml_doc.
  • Затем файл DTD book.dtd загружается как часть построения экземпляра XML :: LibXML :: Dtd.
  • Наконец, сама проверка выполняется с помощью метода validate.

Чтобы троичное выражение в нижней части кода было выполнено, мы используем функции try и catch, которые экспортируются Try :: Tiny. Эти две функции будут обрабатывать любые ошибки, вызванные методом validate. Отсутствие обработки ошибок, возникающих во время вызова validate, приведет к остановке выполнения сценария, поэтому тернарная операция не будет выполнена.

Ошибки проверки

Теперь, если вы запустите perl ./bin/dtd_validation.pl и не исправили XML до этого момента, вы заметите аналогичный вывод в своей оболочке, как показано ниже:

==> ./xml/book.xml:0: validity error : Element book content does not follow the DTD, expecting (title , author , numPages?), got (title numPages )
Invalid

Выходное сообщение указывает, что дочерние элементы элемента book не соответствуют ожидаемым. Ожидается проверка title, author и, возможно, numPages (в указанном порядке), но вместо этого она получила только title и numPages.

Исправление ошибки

Просто добавив элемент author сразу после элемента title и присвоив ему значение, например: <author>Johny Bravo</author>, проверка будет успешной при следующем запуске скрипта. Таким образом, будет напечатано Valid.

Проверка XML с помощью XSD

Файл XSD

Файл ./xml/schemas/book.xsd определяет структуру, которой должен придерживаться ./xml/book.xml документ, а именно:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified">
    <xs:element name="book">
        <xs:complexType>
            <xs:sequence>
                <xs:element name = "title" type = "xs:string"/>
                <xs:element name = "author" type = "xs:string"/>
                <xs:element name = "numPages" type = "xs:positiveInteger" minOccurs="0"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

Подобно DTD, определенному ранее, элементы title и author являются обязательными, а другие, numPages - необязательными (обозначены minOccurs="0"). Также следует отметить, что значение numPages должно иметь тип positiveInteger. XSD позволяет определять множество различных типов данных, используя более богатый синтаксис, в то время как DTD этого не делает.

Код

Файл ./bin/xsd_validation.pl содержит следующий код, который я объясню ниже:

#!/usr/bin/env perl
use v5.18;
use warnings;

use XML::LibXML;
use Try::Tiny qw(try catch);
my $xml_doc = XML::LibXML->load_xml(location => './xml/book.xml');
my $xsd_doc = XML::LibXML::Schema->new(location => './xml/schemas/book.xsd');
my $is_xml_valid = try {
    not $xsd_doc->validate($xml_doc);
}
catch {
    say '==> ' . $_;
    return 0;
};
say $is_xml_valid ? 'Valid' : 'Invalid';

Приведенный выше код представляет собой тот же общий процесс, что и код проверки DTD, рассмотренный ранее. Единственные два отличия реализации:

1) Модуль XML :: LibXML :: Schema используется как для загрузки, так и для проверки XML-файла.

2) Метод XML::LibXML::Schema validate фактически возвращает 0, если проверка прошла успешно. Таким образом, я перед вызовом ...validate($xml) ключевое слово not, чтобы вернуть значение false.

Ошибки проверки

Запустите проверку XSD, используя perl ./bin/xsd_validation.pl.

==> ./xml/book.xml:0: Schemas validity error : Element 'numPages': '-200' is not a valid value of the atomic type 'xs:positiveInteger'.
Invalid

Запуск сценария должен дать результат, показанный выше. Ошибка указывает на то, что XSD ожидает положительное целочисленное значение для numPages, но на самом деле XML содержит отрицательное целое число, в данном случае «-200».

Исправление ошибки

Вы, наверное, меня опередили, но просто изменив значение numPages с -200 на 200 и снова запустив скрипт, вы получите Valid.

Заключение

Для проверки XML-документа с помощью двух обсуждаемых модулей XML :: LibXML потребовались только два объекта: вызов метода validate и использование try и catch для обработки любых возможных ошибок.

Если вас интересует проверка, выполненная с помощью тестов Perl, нажмите здесь, чтобы увидеть репозиторий Github, содержащий код для этой статьи.

Спасибо, что прочитали эту статью! Не стесняйтесь оставлять аплодисменты👏, а также рекомендовать эту статью другим.