14.5. Проверка документа XML на соответствие определению DTD

14.5. Проверка документа XML на соответствие определению DTD

Проблема

Требуется проверить документ XML на соответствие DTD.

Решение

Используйте библиотеку Xerces с парсером SAX2 (простой программный XML-интерфейс) или с парсером DOM.

Для проверки документа XML при использовании SAX2 получите SAX2XMLReader, как показано в примере 14.8. Затем включите режим проверки DTD, вызывая метод парсера setFeature() с аргументами xercesc::XMLUni::fgSAX2CoreValidation и true. Наконец, зарегистрируйте обработчик ErrorHandler для получения уведомлений о нарушении DTD и вызовите метод парсера parse(), указывая в качестве его аргумента имя вашего документа XML.

Для проверки документа XML при использовании парсера DOM сначала сконструируйте экземпляр XercesDOMParser. Затем включите режим проверки DTD, вызывая метод парсера setValidationScheme() с аргументом xercesc::XercesDOMParser::Val_Always. Наконец, зарегистрируйте обработчик ErrorHandler для получения уведомлений о нарушении DTD и вызовите метод парсера parse(), указывая в качестве его аргумента имя вашего документа XML.

Здесь я использую класс XercesDOMParser, т.е. XML-парсер, который входил в состав Xerces еще до того, как был разработан интерфейс DOMBuilder парсера DOM уровня 3. Применение XercesDOMParser позволяет немного упростить пример, но при желании вы можете вместо него использовать DOMBuilder. См. обсуждение этого рецепта и рецепт 14.4.

Для примера предположим, что вы модифицируете документ XML animals.xml из примера 14.1 для того, чтобы он содержал ссылку на внешнее определение DTD, как показано в примерах 14.11 и 14.12. Программный код, выполняющий проверку документа с использованием программного интерфейса SAX2, приводится в примере 14.13; программный код, выполняющий проверку этого документа с использованием парсера DOM, приводится в примере 14.14.

Пример 14.11. DTD animals.dtd для файла animals.xml

<!-- DTD для животных цирка Feldman Family Circus -->

<!ELEMENT animalList (animal+)>

<!ELEMENT animal (name, species, dateOfBirth,

                  veterinarian, trainer) >

<!ELEMENT name (#PCDATA)>

<!ELEMENT species (#PCDATA)>

<!ELEMENT dateOfBirth (#PCDATA)>

<!ELEMENT veterinarian EMPTY>

<!ELEMENT trainer EMPTY>

<!ATTLIST veterinarian

 name  CDATA #REQUIRED

 phone CDATA #REQUIRED

>

<!ATTLIST trainer

 name  CDATA #REQUIRED

 phone CDATA #REQUIRED

>

Пример 14.12. Модифицированный файл animals.xml, содержащий DTD

<?xml version="1.0" encodings "UTF-8"?>

<!-- Животные цирка Feldman Family Circus с DTD. -->

<!DOCTYPE animalList SYSTEM "animals.dtd">

 <!- так же, как в примере 14.1 -->

</animalList>

Пример 14.13. Проверка документа animals.xml на соответствие DTD с использованием программного интерфейса SAX2

/*

* Операторы #include из примера 14.8, кроме включения вектора <vector> который

здесь не нужен

*/

#include <stdexcept> // runtime_error

#include <xercesc/sax2/DefaultHandler.hpp>

using namespace std;

using namespace xercesc;

/*

 * Определить XercesInitializer, как это сделано в примере 14.8, и

 * CircusErrorHandler, как это сделано в примере 14.7

 */

int main() {

 try {

  // Инициализировать Xerces и получить парсер

  SAX2 XercesInitializer init;

  auto_ptr<SAX2XMLReader>

   parser(XMLReaderFactory::createXMLReader());

  // Включить режим проверки

  parser->setFeature(XMLUni::fgSAX2CoreValidation, true);

  // Зарегистрировать обработчик ошибок для получения уведомлений о

  // нарушениях DTD

  CircusErrorHandler error;

  parser->setErrorHandler(&error);

  parser->parse("animals.xml");

 } catch (const SAXException& e) {

  cout << "xml error " << toNative(e.getMessage()) << " ";

  return EXIT_FAILURE;

 } catch (const XMLException& e) {

  cout << "xml error " << toNative(e.getMessage()) << " ";

  return EXIT_FAILURE;

 } catch (const exception& e) {

  cout << e.what() << " ";

  return EXIT_FAILURE;

 }

}

Пример 14.14. Проверка документа animals.xml на соответствие DTD animals.dtd с использованием парсера XercesDOMParser

#include <exception>

#include <iostream>  // cout

#include <stdexcept> // runtime_error

#include <xercesc/dom/DOM.hpp>

#include <xercesc/parsers/XercesDOMParser.hpp>

#include <xercesc/sax/HandlerBase.hpp>

#include <xercesc/util/PlatformUtils.hpp>

#include "xerces_strings.hpp" // Пример 14.4

using namespace std;

using namespace xercesc;

/*

 * Определить XercesInitializer, как это сделано в примере 14.8

 * и CircusErrorHandler, как это сделано в примере 14.7

 */

int main() {

 try {

  // Инициализировать Xerces и сконструировать DOM-парсер.

  XercesInitializer init;

  XercesDOMParser parser;

  // Включить режим проверки DTD

  parser.setValidationScheme(XercesDOMParser::Val_Always);

  // Зарегистрировать обработчик ошибок для получения уведомлений о

  // нарушениях схемы

  CircusErrorHandler handler;

  parser.setErrorHandler(&handler);

  // Выполнить синтаксический анализ вместе с проверкой.

  parser.parse("animals.xml");

 } catch (const SAXException& e) {

  cout << "xml error: " << toNative(e.getMessage()) << " ";

  return EXIT_FAILURE;

 } catch (const XMLException& e) {

  cout << "xml error: " << toNative(e.getMessage()) << " ";

  return EXIT_FAILURE;

 } catch (const exception& e) {

  cout << e.what() << " ";

  return EXIT_FAILURE;

 }

}

Обсуждение

Определения DTD обеспечивают простой способ наложения ограничений на документ XML. Например, в DTD можно указать, какие элементы допускаются в документе, какие атрибуты может иметь элемент и может ли конкретный элемент содержать дочерние элементы, текст или и то и другое. Можно также накладывать ограничения на тип, порядок следования и количество дочерних элементов, а также на значения атрибутов.

DTD предназначены для определения подмножества правильно сформированных документов XML, которые характерны для определенной прикладной области. Например, в примере 14.1 важно то, что каждый элемент animal имеет дочерние элементы name, species, dateOfBirth, veterinarian и trainer, а элементы name, species и dateOfBirth содержат только текст в то время, как элементы veterinarian и trainer имеют атрибуты name и phone. Более того, элемент animal не должен иметь атрибут phone, а элемент veterinarian не должен иметь дочерний элемент species.

DTD в примере 14.11 накладывает ограничения различного типа. Например, приведенное ниже объявление элемента устанавливает необходимость наличия в элементе животного дочерних элементов name, species, dateOfBirth, veterinarian и trainer, задаваемых именно в этом порядке.

<!ELEMENT animal (name, species, dateOfBirth,

                  veterinarian, trainer) >

Аналогично приведенное ниже объявление атрибута указывает на то, что элемент trainer должен иметь атрибуты name и phone, а отсутствие в DTD объявлений других атрибутов для элемента дрессировщика говорит о том, что этот элемент может иметь только два атрибута.

<!ATTLIST trainer

 name  CDATA #REQUIRED

 phone CDATA #REQUIRED

>

Документ XML, который содержит DTD и удовлетворяет его требованиям, называют достоверным (valid). XML-парсер, который обнаруживает не только синтаксические ошибки, но и проверяет достоверность документа XML. называется подтверждающим парсером (validating parser). Хотя парсеры SAX2XMLReader и XercesDOMParser не являются по умолчанию подтверждающими парсерами, в каждом из них предусмотрена функция подтверждения достоверности, которая может подключаться так, как это сделано в примерах 14.13 и 14.14. Аналогично парсер DOMBuilder, описанный в рецепте 14 4, может проверять достоверность документа XML, вызывая свой метод setFeaturе() с аргументами fgXMLUni::fgDOMValidation и true.

Классы SAX2XMLReader, DOMBuilder, DOMWriter и XercesDOMParser поддерживают ряд дополнительных функций. В SAX2XMLReader и DOMBuilder вы можете включать эти функции, используя методы setFeature() и setProperty(). Первый метод принимает строку и булево значение: второй метод принимает строку и void*. Запросить включенные функции можно с помощью методов getFeature() и getProperty(). Для удобства в Xerces предусмотрены константы с именами фикций и свойств. Класс DOMWriter поддерживает setFeature(), но не поддерживает setProperty(). Класс XercesDOMParser поддерживает оба метода, в нем предусмотрены отдельные методы по установке и получению каждой функции. В документации Xerces вы найдете полный список поддерживаемых дополнительных функций.

Смотри также

Рецепт 14.6.