C++. Сборник рецептов - Д. Стефенс
Шрифт:
Интервал:
Закладка:
return EXIT_FAILURE;
}
// Сконструировать DOMBuilder для анализа документа animals.xml.
DOMPtr<DOMBuilder> parser =
static_cast<DOMImplementationLS*>(impl)->
createDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0);
// Подключить пространства имен (они не требуются в этом примере)
parser->setFeature(XMLUni::fgDOMNamespaces, true);
// Зарегистрировать обработчик ошибок
CircusErrorHandler err;
parser->setErrorHandler(&err);
// Выполнить синтаксический анализ animals.xml; здесь можно
// использовать URL вместо имени файла
DOMDocument* doc =
parser->parseURI("animals.xml");
// Найти элемент слона Herby: сначала получить указатель на элемент
// "animalList".
DOMElement* animalList = doc->getDocumentElement();
if (animalList->getTagName() != fromNative("animalList")) {
cout << "bad document root: "
<< toNative(animalist->getTagName()) << "n";
return EXIT_FAILURE;
}
// Затем просматривать элементы "animal", пытаясь найти элемент слона
// Herby.
DOMNodeList* animals =
animaIList->getElementsByTagName(fromNative("animal").c_str());
for (size_t i = 0,
len = animals->getLength(); i < len; ++i) {
DOMElement* animal =
static_cast<DOMElement">(animals->item(i));
const XMLCh* name = getAnimalName(animal);
if (name != 0 && name == fromNative("Herby")) {
// Herby найден - удалить его из документа.
animalList->removeChild(animal);
animal->release();
// необязательный оператор.
break;
}
}
// Сконструировать DOMWriter для сохранения animals.xml.
DOMPtr<DOMWriter> writer =
static cast<DOMImplementationLS*>(impl)->createDOMWriter();
writer->setErrorHandler(&err);
// Сохранить animals.xml.
LocalFileFormatTarget file("animals.xml");
writer->writeNode(&file, *animalList);
} catch (const SAXException& e) {
cout << "xml error: " << toNative(e.getMessage()) << "n";
return EXIT_FAILURE;
} catch (const DOMException& e) {
cout << "xml error: " << toNative(e.getMessage()) << "n";
return EXIT_FAILURE;
} catch (const exception& e) {
cout << e.what() << "n";
return EXIT_FAILURE;
}
}
ОбсуждениеПодобно парсеру TinyXml парсер Xerces DOM на выходе формирует документ XML в виде объекта C++, имеющего структуру дерева, узлы которого представляют компоненты документа. Однако парсер Xerces существенно сложнее: например, в отличие от TinyXml он «понимает» пространства имен XML и может выполнять синтаксический анализ сложных DTD. Этим парсером также формируется гораздо более детальное представление документа XML, включающее инструкции обработки и URI пространств имен, относящиеся к элементам и атрибутам. Очень важно то, что он предоставляет доступ к информации через интерфейс, описанный в спецификации W3C DOM.
Спецификация W3C, которая все еще дорабатывается, имеет несколько «уровней»; в настоящее время предусматривается три уровня. Классы DOMImplementation, DOMDocument, DOMElement и DOMNodeList, использованные в примере 14.10, описываются на уровне 1 спецификации DOM. Классы DOMBuilder и DOMWrite описываются на уровне 3 спецификации DOM как часть рекомендаций по функции загрузки и сохранения (Load и Save).
Имена классов Xerces не всегда совладают с именами интерфейсов W3C DOM, которые они реализуют; это происходит из-за того, что Xerces реализует несколько спецификаций в одном пространстве имен и использует префиксы в именах классов, чтобы избежать конфликтов имен.
Понимание примера 14.10 теперь не должно вызывать затруднений. Я начинаю с инициализации Xerces, как это делается в примере 14.8. Затем я получаю DOMImplementation из DOMImplementationRegistry, запрашивая функцию загрузки и сохранения путем передачи строки «LS» статическому методу DOMImplementationRegistry::getDOMImplementation(). На следующем шаге я получаю DOMBuilder из DOMImplementation. Мне приходится тип DOMImplementation привести к типу DOMImplementationLS, потому что функция загрузки и сохранения недоступна из интерфейса DOMImplementation согласно спецификации W3C DOM уровня 1. Первый аргумент createDOMBuilder() показывает, что возвращаемый парсер будет работать в синхронном режиме. Другой возможный режим, а именно асинхронный режим, в настоящее время не поддерживается в Xerces.
Получив DOMBuilder, я включаю поддержку пространства имен XML, регистрирую обработчик ErrorHandler и выполняю синтаксический анализ документа. Парсер возвращает документ в виде DOMDocument; используя метод getElementsByTagName() документа DOMDocument, я получаю объект DOMElement, соответствующий элементу этого документа animalList, и просматриваю его дочерние элементы, используя объект типа DOMNodeList. Когда я нахожу элемент, имеющий дочерний элемент типа name и содержащий текст «Herby», я удаляю его из документа с помощью вызова метода корневого элемента removeChild().
Подобно тому как SAX2XMLReader имеет метод parse(), принимающий экземпляр InputSource, DOMBuilder имеет метол parse(), принимающий экземпляр xercesc::DOMInputSource (т.е. экземпляр абстрактного класса, который инкапсулирует источник символьных данных). В DOMInputSource предусмотрен конкретный подкласс Wrapper4DOMInputSource, который может быть использован для преобразования произвольного InputSource в xercesc::DOMInputSource. См рецепт 14.3.
Наконец, я получаю объект DOMWriter из DOMImplementation (причем делаю это во многом точно так же, как при получении объекта DOMBuilder) и сохраняю модифицированный документ XML на диск, вызывая его метод writeNode(), передавая в качестве аргумента корневой элемент документа.
Вы должны освободить указатели, возвращаемые методами с именами вида DOMImplementation::createXXX(), путем вызова метода release(). Используйте утилиту DOMPtr из примера 14.10 для того, чтобы гарантировать освобождение такого указателя, даже если выбрасывается исключение. Необязательно явно удалять указатели, возвращаемые методами, имена которых имеют вид DOMDocument::createXXX(), хотя это можно делать, если они больше не нужны. Дополнительные сведения вы найдете в документации Xerces.
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