Д. Стефенс - C++. Сборник рецептов
parsingAnimal_ = true; // Обновить состояние
// анализа.
animalList_.push_back(Animal()); // Добавить в список объект
// Animal.
} else {
// Неправильная вложенность
throw runtime error(
string("expected 'animal', got ") + toNative(localname)
);
}
} else {
// Вы находимся в середине анализа элемента, описывающего
// животного.
if (parsingAnimalChild_) {
// Неправильная вложенность
throw runtime_error("bad animal element");
}
// Обновить состояние анализа
parsingAnimalChild_ = true;
// Пусть startAnimalChild() выполнит реальную работу
startAnimalChild(uri, localname, qname, attrs);
}
}
void endElement(
const XMLCh *const uri, // uri пространства имен
const XMLCh *const localname, // простое имя тега
const XMLCh *const qname ) // квалифицированное имя тега
{
static XercesString animalList = fromNative("animal-list");
static XercesString animal = fromNative("animal");
// Обновить флаги parsingAnimalList, parsingAnimal_
// и parsingAnimalChild_; делегировать основную работу
// endAnimalChild()
if (localname == animal) {
parsingAnimal_ = false;
} else if (localname == animalList) {
parsingAnimalList_ = false;
} else {
endAnimalChild(uri, localname, qname);
parsingAnimalChild_ = false;
}
}
// Получает уведомления о встрече символьных данных
void characters(const XMLCh* const chars, const unsigned int length) {
// Добавляет символы в конец currentText_ для обработки методом
// endAnimalChild()
currentText.append(chars, length);
}
private:
// Если текущий элемент представляет ветеринара или дрессировщика,
// используйте attrs для конструирования объекта Contact для
// текущего Animal; в противном случае очистите currentText_,
// подготавливая обратный вызов characters()
void startAnimalChild(
const XMLCh *const uri, // uri пространства имен
const XMLCh *const localname, // простое имя тега
const XMLCh *const qname, // квалифицированное имя тега
const Attributes &attrs ) // Набор атрибутов
{
static XercesString vet = fromNative("veterinarian");
static XercesString trainer = fromNative("trainer");
Animal& animal = animalList_.back();
if (localname == vet) {
// Мы встретили элемент "ветеринар".
animal.setVeterinarian(contactFromAttributes(attrs));
} else if (localname == trainer) {
// Мы встретили элемент "дрессировщик".
animal.setTrainer(contactFromAttributes(attrs));
} else {
// Мы встретили элемент "кличка , "вид" или
// "дата рождения". Его содержимое будет передано функцией
// обратного вызова characters().
currentText_.clear();
}
}
// Если текущий элемент представляет кличку, вид или дату рождения,
// используйте текст, находящийся в currentText_, для установки
// соответствующего свойства текущего объекта
Animal. void endAnimalChild(
const XMLCh *const uri, // uri пространства имен
const XMLCh *const localname, // простое имя тега
const XMLCh *const qname) // квалифицированное имя тега
{
static XercesString name = fromNative("name");
static XercesString species = fromNative("species");
static XercesString dob = fromNative("dateOfBirth");
// currentText_ содержит текст элемента, который только что
// закончился. Используйте его для установки свойств текущего
// объекта Animal.
Animal& animal = animalList_.back();
if (localname == name) {
animal.setName(toNative(currentText_));
} else if (localname == species) {
animal.setSpecies(toNative(currentText_));
} else if (localname == dob) {
animal.setDateOfBirth(toNative(currentText_));
}
}
vector<Animal>& animalList_; // заполняемый список
bool parsingAnimalList_; // состояние анализа
bool parsingAnimal_; // состояние анализа
bool parsingAnimalChild_; // состояние анализа
XercesString currentText_; // символьные данные текущего
// текстового узла
};
Из сравнения примера 14.9 с примером 14.6 видно, насколько сложным может быть проверка структуры документа с помощью функций обратного вызова. Более того, в примере 14.6 не делается столько проверок, как в примере 14.3: здесь, например, не проверяется порядок следования дочерних элементов элемента животного. К счастью, существует гораздо более простой способ проверки структуры документа с использованием SАХ2, как вы это увидите в рецептах 14.5 и 14.6.
Смотри такжеРецепты 14.1, 14.4, 14.5 и 14.6.
14.4. Манипулирование документом XML
Требуется представить документ XML в виде объекта С++, чтобы можно было манипулировать его элементами, атрибутами, текстом, DTD, инструкциями обработки и комментариями
РешениеИспользуйте реализованную в Xerces модель W3C DOM. Во-первых, используйте класс xercesc::DOMImplementationRegistry для получения экземпляра xercesc::DOMImplementation, затем используйте DOMImplementation для создания экземпляра парсера xercesc::DOMBuilder. На следующем шаге зарегистрируйте экземпляр xercesc::DOMErrorHandler для получения уведомлений об ошибках, обнаруженных в ходе анализа, и вызовите метод парсера parseURI(), передавая в качестве аргумента URI документа XML или полное имя файла. Если анализ документа завершается успешно, метод parseURI возвратит указатель на объект DOMDocument, представляющий документ XML. Затем вы можете использовать функции, определенные в спецификации W3C DOM для просмотра и манипулирования документом.
Обработав документ, вы можете сохранить его в файле, получая DOMWriter из DOMImplementation и вызывая его метод writeNode() с передачей указателя на DOMDocument в качестве аргумента.
Пример 14.10 показывает, как можно использовать DOM для синтаксического анализа документа animals.xml, приведенного в примере 14.1, затем найти и удалить узел, относящийся к слону Herby, и сохранить модифицированный документ.
Пример 14.10. Применение DOM для загрузки, модификации и затем сохранения документа XML
#include <exception>
#include <iostream> // cout
#include <xercesc/dom/DOM.hpp>
#include <xercesc/framework/LocalFileFomatTarget.hpp>
#include <xercesc/sax/SAXException.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include "animal.hpp"
#include "xerces_strings.hpp"
using namespace std;
using namespace xercesc;
/*
* Определить XercesInitializer, как это сделано в примере 14.8
*/
// Утилита RAII, которая освобождает ресурс при выходе из области видимости.
template<typename T>
class DOMPtr {
public:
DOMPtr(T* t) : t_(t) {}
~DOMPtr() { t_->release(); }
T* operator->() const { return t_; }
private:
// запретить копирование и присваивание
DOMPtr(const DOMPtr&);
DOMPtr& operator=(const DOMPtr&);
T* t_;
};
// Сообщает об ошибках, обнаруженных в ходе синтаксического анализа с
// использованием DOMBuilder.
class CircusErrorHandler : public DOMErrorHandler {
public:
bool handleFrror(const DOMError& e) {
std::cout << toNative(e.getMessage()) << "n";
return false;
}
};
// Возвращает значение элемента "name", дочернего по отношению к элементу
// "animal".
const XMLCh* getAnimalName(const DOMElement* animal) {
static XercesString name = fromNative("name");
// Просмотреть дочерние элементы объекта animal
DOMNodeList* children = animal->getChildNodes();
for (size_t i = 0, len = children->getLength(); i < Len; ++i) {
DOMNode* child = children->item(i);
if (child->getNodeType() == DOMNode::ELEMENT_NODE &&
static_cast<DOMElement*>(child)->getTagName() == name) {
// Мы нашли элемент "name".
return child->getTextContent();
}
}
return 0;
}
int main() {
try {
// Инициализировать Xerces и получить DOMImplementation;
// указать, что требуется функция загрузки и сохранения (Load and
// Save - LS)
XercesInitializer init;
DOMImplementation* impl =
DOMImplementationRegistry::getDOMImplementation(fromNative("LS").c_str()
);
if (impl == 0) {
cout << "couldn't create DOM implementationn";