====== 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; }