Różnice między wybraną wersją a wersją aktualną.
Both sides previous revision Previous revision Next revision | Previous revision Next revision Both sides next revision | ||
xerces_dom [2008/04/15 00:37] kmioduszewski |
xerces_dom [2008/04/16 02:14] kmioduszewski |
||
---|---|---|---|
Linia 1: | Linia 1: | ||
====== biblioteka dla XML: Xerces (DOM) ===== | ====== biblioteka dla XML: Xerces (DOM) ===== | ||
- | **Autor:** //[[kmioduszewski@polibuda.info|Krzysztof Mioduszewski]] 2008/04/14 23:59//\\ \\ | + | **Autor** //[[kmioduszewski@polibuda.info|Krzysztof Mioduszewski]] 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. | 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. | ||
\\ \\ | \\ \\ | ||
Linia 139: | Linia 139: | ||
</code> | </code> | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
Linia 152: | Linia 162: | ||
===== DOMDocument, operacje na dokumencie===== | ===== 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żje znajduje się kod przykładowej aplikacji, która wczytuje plik XML, wpisuje jego zawartość na ekran, modyfikuje go, a następnie zapisuje zmiany do pliku.\\ \\ | + | 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: | Plik XML - ''notes.xml'', który zostanie wczytany: | ||
<code xml> | <code xml> | ||
Linia 158: | Linia 168: | ||
<!DOCTYPE notes SYSTEM "note.dtd"> | <!DOCTYPE notes SYSTEM "note.dtd"> | ||
<notes owner="John Doe"> | <notes owner="John Doe"> | ||
- | |||
<note date="21/02/2008"> | <note date="21/02/2008"> | ||
<from>Jani</from> | <from>Jani</from> | ||
Linia 181: | Linia 190: | ||
<!ELEMENT body (#PCDATA)> | <!ELEMENT body (#PCDATA)> | ||
</code> | </code> | ||
- | Klasy pomocnicze: | + | Klasy pomocnicze ''support.h'': |
<code cpp> | <code cpp> | ||
+ | #include <string> | ||
+ | #include <iostream> | ||
+ | #include <stdexcept> | ||
#include <xercesc/util/XMLString.hpp> | #include <xercesc/util/XMLString.hpp> | ||
+ | #include <xercesc/sax/HandlerBase.hpp> | ||
+ | #include <xercesc/util/PlatformUtils.hpp> | ||
+ | #include <xercesc/dom/DOM.hpp> | ||
+ | #include <xercesc/parsers/XercesDOMParser.hpp> | ||
+ | #include <xercesc/framework/LocalFileFormatTarget.hpp> | ||
+ | #include <xercesc/framework/StdOutFormatTarget.hpp> | ||
- | //klasa pomocnicza przechowujaca nazwy tagów oraz atrybutów w formacie XMLCh* | + | //klasa pomocnicza ułatwiająca współpracę z XMLCh* |
- | class TagNames { | + | class UniString { |
- | public: | + | private : |
- | XMLCh* TAG_NOTES; | + | char* cString; |
- | XMLCh* TAG_NOTE; | + | XMLCh* xmlString; |
- | XMLCh* TAG_FROM; | + | |
- | XMLCh* TAG_HEADING; | + | |
- | XMLCh* TAG_BODY; | + | |
- | XMLCh* ATTR_OWNER; | + | public : |
- | XMLCh* ATTR_DATE; | + | 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); | ||
+ | } | ||
+ | |||
- | TagNames() | + | }; |
+ | |||
+ | 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(xercesc::XMLString::transcode("notes")), | + | TAG_NOTES("notes"), |
- | TAG_NOTE(xercesc::XMLString::transcode("note")), | + | TAG_NOTE("note"), |
- | TAG_FROM(xercesc::XMLString::transcode("from")), | + | TAG_FROM("from"), |
- | TAG_HEADING(xercesc::XMLString::transcode("heading")), | + | TAG_HEADING("heading"), |
- | TAG_BODY(xercesc::XMLString::transcode("body")), | + | TAG_BODY("body"), |
- | + | ||
- | ATTR_OWNER(xercesc::XMLString::transcode("owner")), | + | ATTR_OWNER("owner"), |
- | ATTR_DATE(xercesc::XMLString::transcode("date")) | + | ATTR_DATE("date") |
{} | {} | ||
+ | }; | ||
- | ~TagNames() throw(){ | + | 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: " <<e.getLineNumber() << std::endl; | ||
+ | } | ||
+ | void resetErrors() {} | ||
+ | }; | ||
+ | </code> | ||
+ | ''DOMtest.h'' - zawiera klasę DOMtest, prezentującą możliwości korzystania biblioteki DOM. | ||
+ | <code cpp> | ||
+ | #include "support.h" | ||
- | try{ | + | class DOMtest { |
- | xercesc::XMLString::release(&TAG_NOTES); | + | private: |
- | xercesc::XMLString::release(&TAG_NOTE); | + | std::string filename; |
- | xercesc::XMLString::release(&TAG_FROM); | + | xercesc::XercesDOMParser parser; |
- | xercesc::XMLString::release(&TAG_HEADING); | + | DocNames names; |
- | xercesc::XMLString::release(&TAG_BODY); | + | 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("...")); | ||
+ | } | ||
- | xercesc::XMLString::release(&ATTR_OWNER); | + | //funkcja zawierający przykładowy sposób pobiernia |
- | xercesc::XMLString::release(&ATTR_DATE); | + | //informacji z dokumentu (bonus wyswietlanie ich) |
- | }catch( ... ){ | + | std::ostream& print(std::ostream& s) throw(std::runtime_error) { |
- | std::cerr << "TagNames destructor error" << std::endl ; | + | s << "MY NOTES: " << std::endl; |
- | } | + | try { |
- | } | + | //bierzmy sobie dokumenty |
- | } | + | xercesc::DOMDocument* xmlDoc = parser.getDocument(); |
- | </code> | + | |
- | <code cpp> | + | //bierzemy sobie element główny |
- | //... | + | xercesc::DOMElement* root = xmlDoc->getDocumentElement(); |
+ | |||
+ | if(root == NULL) | ||
+ | throw(std::runtime_error("Empty Document")); | ||
- | xercesc::DOMDocument* XMLdoc = parser->getDocument(); | + | //pobieramy wartość atrybutu owner |
- | xercesc::DOMElement* root = XMLdoc->getDocumentElement(); | + | //names.ATTR_OWNER.toXMLString() <- zwraca XMLCh* "owner"; -> support.h |
+ | UniString owner(root->getAttribute(names.ATTR_OWNER.toXMLString())); | ||
+ | s << "owner: " << owner << std::endl; | ||
- | //pobieramy nazwę elementu głównego | + | //pobieramy wszystkie elementy 'note' |
- | char* rootName = XMLString::transcode(root->getLocalName()); | + | xercesc::DOMNodeList* notesElements = root->getElementsByTagName(names.TAG_NOTE.toXMLString()); |
- | if(root->hasAttributes()) { | + | for( XMLSize_t i = 0; i < notesElements->getLength(); ++i) { |
- | xercesc::DOMNamedNodeMap* attrMap = root->getAttributes(); | + | s << "note no. " << i+1 << " "; |
- | //iterujemy sobie po atrybutach | + | |
- | for( XMLSize_t i = 0 ; i < attrMap->getLength() ; ++i ) { | + | //bierzmy sobie node |
- | DOMAttr* attr = dynamic_cast<DOMAttr*>(attrMap->item(ix)); | + | xercesc::DOMNode* node = notesElements->item(i); |
- | //wykorzystujemy sobie atrybut | + | |
- | attr->getName(); //tutaj mamy nazwe | + | //konwersja na DOMElement, mogę sobie na to pozwolić gdyż znam, |
- | attr->getValue(); //wartosc | + | //całą strukturę dokumentu, w przypadku nie znania wypadałoby sprawdzić |
- | //... | + | //aczkolwiek dynamic_cast rzuci wyjatke w razie problemów |
+ | xercesc::DOMElement* domElement = dynamic_cast<xercesc::DOMElement*>(node); | ||
+ | |||
+ | //pobieram datę | ||
+ | UniString date(domElement->getAttribute(names.ATTR_DATE.toXMLString())); | ||
+ | s << "date: " << date << std::endl; | ||
+ | |||
+ | //pobiorę sobie "bezposrednio" zawartość <form></form> | ||
+ | //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 <note></note> | ||
+ | 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 <from></from> | ||
+ | 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 <notes></notes> | ||
+ | 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())); | ||
+ | } | ||
+ | } | ||
+ | }; | ||
+ | </code> | ||
+ | plik testujący ''main.cpp'' | ||
+ | <code 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 = new DOMtest("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"); | ||
+ | |||
+ | delete(DOM); | ||
+ | } catch(std::runtime_error& e) { | ||
+ | std::cerr << e.what() << std::endl; | ||
+ | std::cerr << "Exiting" << std::endl; | ||
+ | } | ||
+ | |||
+ | xercesc::XMLPlatformUtils::Terminate(); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
</code> | </code> |