====== biblioteka dla XML: Xerces (DOM) =====
**Autor** //Krzysztof Mioduszewski - k.mioduszewski@stud.elka... 2008/04/16 02:12//\\ \\
Xerces-C++ DOM jest częścią Xerces-C++ API zaprojektowanego do współpracy z plikami w formacie XML. API umożliwia parsowania, manipulowanie, walidowanie oraz generowanie poprawnych dokumentów.
\\ \\
Biblioteka DOM jest implementacją następujących rekomendacji W3C:
* [[http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/|Document Object Model (DOM) Level 1 Specification]]
* [[http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/|Document Object Model (DOM) Level 2 Core Specification]]
* [[http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/|Document Object Model (DOM) Level 2 Traversal and Range Specification]]
Oraz zawiera częściową implementację (opartą o W3C Working Draft 09 April 2002) :
* [[http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20020409/|Document Object Model (DOM) Level 3 Core Specification]]
* [[http://www.w3.org/TR/2002/WD-DOM-Level-3-ASLS-20020409/|Document Object Model (DOM) Level 3 Abstract Schemas and Load and Save Specification]]
\\
Więcej informacji na stronie projektu http://xerces.apache.org/xerces-c/.
Na stronie znajdują się źródła, oraz skompilowane wersje biblioteki na różne platformy. Kompletna instrukcja instalacji na różnych platformach znajduje się [[http://xerces.apache.org/xerces-c/install.html|tutaj]].
===== Informacje wstępne =====
Nagłówki, które powinniśmy dołączyć w przypadku pisania typowych aplikacji korzystających z Xerces DOM znajdują się poniżej:
#include
#include
#include
#include
Xerces posiada własny typ znakowy ''XMLCh'', który jest używany zamiast ''char'' i ''std::string''. Do konwersji pomiędzy typami ''char*'' a ''XMLCh*'' (w obie strony) używana jest funkcja ''XMLString::transcode()''. Pamięć zaalokowaną na łańcuchy należy zwalniać za pomocą funkcji ''XMLString::release()''.
Przed użyciem jakichkolwiek klas Xerces konieczne jest wywołanie metody ''XMLPlatformUtils::Initialize()''. Po zakończeniu korzystania z Xerces powinno się wywołać ''XMLPlatformUtils::Terminate()''. Poniżej znajduje się uproszczona struktura programu (warto zwrócić uwagę na uzycie ''transcode()'').
#include
#include
#include
#include
// ...
#include
int main()
{
try {
//incjalizacja Xerces-C++
xercesc::XMLPlatformUtils::Initialize();
}
catch (const xercesc::XMLException& e) {
//konwersja z XMLCh* na char*
char* msg = xercesc::XMLString::transcode(e.getMessage()) ;
std::cerr << "Init Error: " << msg << std::endl;
//zwolnienie pamieci
xercesc::XMLString::release(&msg) ;
return 1;
}
// Tutaj korzystamy z mozliwosci Xerces-C++
xercesc::XMLPlatformUtils::Terminate();
return 0;
}
===== XercesDOMParser =====
Jest to parser umożliwiający wczytanie do pamięci struktury pliku XML za pomocą metody ''parse()''. Po czym uzyskujemy dostęp do obiektu ''DOMDocument'', po którym możemy poruszać się wykorzystując zaimplementowane w zgodzie z rekomendacjami W3C metody.
Poniżej przykładowe fragmenty kodu dotyczącego inicjalizacji i korzystania z ''XercesDOMParser''.
xercesc::XercesDOMParser* parser = new xercesc::XercesDOMParser();
parser->setValidationScheme(xercesc::XercesDOMParser::Val_Auto); //uruchamia walidację, jeżeli dostępne jest DTD (wewnętrzne/zewnętrzne)
parser->setDoNamespaces(true);
char* xmlFile = "file.xml";
try {
parser->parse(xmlFile);
}
catch( const xercesc::XMLException& e ){
//...
}
catch( const xercesc::DOMException& e ){
//...
}
catch (...) {
//...
}
//sprawdzamy czy nie wystąpiły błędy podczas parsowania
if(parser->getErrorCount()==0) {
xercesc::DOMDocument* XMLdoc = parser->getDocument();
//...
}
Dobrym pomysłem jest zastosowanie własnego ''ErrorHandler'' do wykrywania błędów składniowych w trakcie parsowania i wyświetlania o nich informacji (typ, miejsce wystąpienia). Najprostszy sposób to zaimplementowanie metod z interfejsu ''HandlerBase'':
#include
class myErrorHandler : xercesc::HandlerBase {
void warning( const xercesc::SAXParseException& e ) {
char *msg = xercesc::XMLString::transcode(e.getMessage());
std::cout << "warning: " << msg
<< " | line: " << e.getLineNumber() << std::endl;
xercesc::XMLString::release(&msg);
}
void error( const xercesc::SAXParseException& e ) {
// analogicznie jak wyżej
}
void fatalError( const xercesc::SAXParseException& e ) {
// j.w.
}
void resetErrors() {}
};
//...
xercesc::XercesDOMParser* parser = new xercesc::XercesDOMParser();
parser->setValidationScheme(xercesc::XercesDOMParser::Val_Auto);
parser->setDoNamespaces(true);
xercesc::ErrorHandler* errHandler = (xercesc::ErrorHandler*) new myErrorHandler();
parser->setErrorHandler(errHandler);
===== DOMDocument, operacje na dokumencie=====
Gdy dokument zostanie sparsowany bez żadnych błędów możemy swobodnie zacząć na nim pracować wykorzystując zawarte w rekomendacjach metody. Poniżej znajduje się kod przykładowej aplikacji, która wczytuje plik XML, wypisuje jego zawartość na ekran, modyfikuje go, a następnie zapisuje zmiany do pliku.\\ \\
Plik XML - ''notes.xml'', który zostanie wczytany:
Jani
Reminder
Don't forget me this weekend
Jeff
Money
Gimme my money back!
DTD - ''notes.dtd''
Klasy pomocnicze ''support.h'':
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//klasa pomocnicza ułatwiająca współpracę z XMLCh*
class UniString {
private :
char* cString;
XMLCh* xmlString;
public :
UniString(const XMLCh* const str)
:
cString(xercesc::XMLString::transcode(str)),
xmlString(xercesc::XMLString::replicate(str))
{}
UniString(const char* const str)
:
cString(xercesc::XMLString::replicate(str)),
xmlString(xercesc::XMLString::transcode(str))
{}
UniString(const std::string& str)
:
cString(xercesc::XMLString::replicate(str.c_str())),
xmlString(xercesc::XMLString::transcode(str.c_str()))
{}
~UniString()
{
xercesc::XMLString::release(&cString);
xercesc::XMLString::release(&xmlString);
}
const char* toCString() const { return cString; }
const XMLCh* toXMLString() const { return xmlString; }
std::ostream& print(std::ostream& s) const
{
s << cString;
return(s);
}
};
std::ostream& operator<<( std::ostream& s, const UniString& str ) { return str.print(s); }
//klasa pomocnicza przechowująca nazwy tagów oraz atrybutów
class DocNames {
public:
const UniString TAG_NOTES;
const UniString TAG_NOTE;
const UniString TAG_FROM;
const UniString TAG_HEADING;
const UniString TAG_BODY;
const UniString ATTR_OWNER;
const UniString ATTR_DATE;
DocNames()
:
TAG_NOTES("notes"),
TAG_NOTE("note"),
TAG_FROM("from"),
TAG_HEADING("heading"),
TAG_BODY("body"),
ATTR_OWNER("owner"),
ATTR_DATE("date")
{}
};
class myErrorHandler : xercesc::HandlerBase {
void warning( const xercesc::SAXParseException& e ) {
std::cerr << "warning: " << UniString(e.getMessage())
<< " | line: " << e.getLineNumber() << std::endl;
}
void error( const xercesc::SAXParseException& e ) {
std::cerr << "error: " << UniString(e.getMessage())
<< " | line: " << e.getLineNumber() << std::endl;
}
void fatalError( const xercesc::SAXParseException& e ) {
std::cerr << "fatalError: " << UniString(e.getMessage())
<< " | line: " <
''DOMtest.h'' - zawiera klasę DOMtest, prezentującą możliwości korzystania biblioteki DOM.
#include "support.h"
class DOMtest {
private:
std::string filename;
xercesc::XercesDOMParser parser;
DocNames names;
bool loaded;
DOMtest(const DOMtest&);
public:
DOMtest(std::string f)
:
filename(f),
parser(),
names(),
loaded(false)
{ };
void init() throw( std::runtime_error )
{
parser.setValidationScheme(xercesc::XercesDOMParser::Val_Auto);
parser.setDoNamespaces(true);
xercesc::ErrorHandler* errHandler = (xercesc::ErrorHandler*) new myErrorHandler();
parser.setErrorHandler(errHandler);
try {
parser.parse(filename.c_str());
} catch(xercesc::XMLException& e){
UniString eMsg(e.getMessage());
throw(std::runtime_error(eMsg.toCString()));
}catch( const xercesc::DOMException& e ){
UniString eMsg(e.getMessage());
throw(std::runtime_error(eMsg.toCString()));
}
if(parser.getErrorCount()!=0)
throw(std::runtime_error("..."));
}
//funkcja zawierający przykładowy sposób pobiernia
//informacji z dokumentu (bonus wyswietlanie ich)
std::ostream& print(std::ostream& s) throw(std::runtime_error) {
s << "MY NOTES: " << std::endl;
try {
//bierzmy sobie dokumenty
xercesc::DOMDocument* xmlDoc = parser.getDocument();
//bierzemy sobie element główny
xercesc::DOMElement* root = xmlDoc->getDocumentElement();
if(root == NULL)
throw(std::runtime_error("Empty Document"));
//pobieramy wartość atrybutu owner
//names.ATTR_OWNER.toXMLString() <- zwraca XMLCh* "owner"; -> support.h
UniString owner(root->getAttribute(names.ATTR_OWNER.toXMLString()));
s << "owner: " << owner << std::endl;
//pobieramy wszystkie elementy 'note'
xercesc::DOMNodeList* notesElements = root->getElementsByTagName(names.TAG_NOTE.toXMLString());
for( XMLSize_t i = 0; i < notesElements->getLength(); ++i) {
s << "note no. " << i+1 << " ";
//bierzmy sobie node
xercesc::DOMNode* node = notesElements->item(i);
//konwersja na DOMElement, mogę sobie na to pozwolić gdyż znam,
//całą strukturę dokumentu, w przypadku nie znania wypadałoby sprawdzić
//aczkolwiek dynamic_cast rzuci wyjatke w razie problemów
xercesc::DOMElement* domElement = dynamic_cast(node);
//pobieram datę
UniString date(domElement->getAttribute(names.ATTR_DATE.toXMLString()));
s << "date: " << date << std::endl;
//pobiorę sobie "bezposrednio" zawartość
//nie jest to może tak "bezpośrednie" jak np. w Javascript ;)
UniString from(domElement->getElementsByTagName(names.TAG_FROM.toXMLString())->item(0)->getTextContent());
s << " From: " << from << std::endl;
//teraz kolejne elementy
UniString heading(domElement->getElementsByTagName(names.TAG_HEADING.toXMLString())->item(0)->getTextContent());
s << " Heading: " << heading << std::endl;
UniString body(domElement->getElementsByTagName(names.TAG_BODY.toXMLString())->item(0)->getTextContent());
s << " Body: " << std::endl << " " << body << std::endl << std::endl;
}
} catch(xercesc::XMLException& e){
UniString eMsg(e.getMessage());
throw(std::runtime_error(eMsg.toCString()));
} catch( const xercesc::DOMException& e ){
UniString eMsg(e.getMessage());
throw(std::runtime_error(eMsg.toCString()));
}
return(s) ;
}
//tworzymy nową notkę i dopinamy ją do drzewa dokumentu
void modify() throw(std::runtime_error)
{
try {
//tresc notki
UniString date("16/04/2008");
UniString from("Cassius");
UniString heading("!!!");
UniString body("Nice try, Zarflax!");
//aby tworzyć nowe elemnty potrzebujemy DOMDocument
xercesc::DOMDocument* xmlDoc = parser.getDocument();
//tworzymy nowy element
xercesc::DOMElement* noteElement = xmlDoc->createElement(names.TAG_NOTE.toXMLString());
//tworzymy atrybut date
xercesc::DOMAttr* noteElementDate = xmlDoc->createAttribute(names.ATTR_DATE.toXMLString());
//ustawiamy wartosc atrybutu date
noteElementDate->setValue(date.toXMLString());
//przypinamy atrybut date do elementu note
noteElement->setAttributeNode(noteElementDate);
//tworzymy kolejne elementy i przypominamy do notki
xercesc::DOMElement* noteFromElement = xmlDoc->createElement(names.TAG_FROM.toXMLString());
//ustawiamy tresc elementu
noteFromElement->setTextContent(from.toXMLString());
noteElement->appendChild(noteFromElement);
xercesc::DOMElement* noteHeadingElement = xmlDoc->createElement(names.TAG_HEADING.toXMLString());
noteHeadingElement->setTextContent(heading.toXMLString());
noteElement->appendChild(noteHeadingElement);
xercesc::DOMElement* noteBodyElement = xmlDoc->createElement(names.TAG_BODY.toXMLString());
noteBodyElement->setTextContent(body.toXMLString());
noteElement->appendChild(noteBodyElement);
//dopinamy kompletną notkę elementu
xmlDoc->getDocumentElement()->appendChild(noteElement);
} catch(xercesc::XMLException& e){
UniString eMsg(e.getMessage());
throw(std::runtime_error(eMsg.toCString()));
} catch( const xercesc::DOMException& e ){
UniString eMsg(e.getMessage());
throw(std::runtime_error(eMsg.toCString()));
}
}
void write(std::string filename) throw(std::runtime_error) {
try {
//konieczne do wykorzystania przy zapisie pliku
xercesc::XMLFormatTarget* file = new xercesc::LocalFileFormatTarget(UniString(filename).toXMLString());
//obiekt wykorzystywana do zapisu
xercesc::DOMWriter* domWriter = parser.getDocument()->getImplementation()->createDOMWriter();
//jezeli mamy możliwość ustawiamy przyjazne dla człowieka formatowanie
if(domWriter->canSetFeature(xercesc::XMLUni::fgDOMWRTFormatPrettyPrint,true)){
domWriter->setFeature(xercesc::XMLUni::fgDOMWRTFormatPrettyPrint,true);
}
//zapis
domWriter->writeNode(file,*parser.getDocument());
delete(file);
} catch(xercesc::XMLException& e){
UniString eMsg(e.getMessage());
throw(std::runtime_error(eMsg.toCString()));
} catch( const xercesc::DOMException& e ){
UniString eMsg(e.getMessage());
throw(std::runtime_error(eMsg.toCString()));
}
}
};
plik testujący ''main.cpp''
#include "DOMtest.h"
int main() {
try {
xercesc::XMLPlatformUtils::Initialize();
}
catch (const xercesc::XMLException& e) {
std::cerr << UniString(e.getMessage()) << std::endl;
return 1;
}
try {
DOMtest DOM("notes.xml");
//incjacja prasera itp.
DOM.init();
//pobieranie wartości, wydruk na ekranie
DOM.print(std::cout);
//dodanie notki
DOM.modify();
//zapisu do pliku
DOM.write("notes2.xml");
} catch(std::runtime_error& e) {
std::cerr << e.what() << std::endl;
std::cerr << "Exiting" << std::endl;
}
xercesc::XMLPlatformUtils::Terminate();
return 0;
}