Narzędzia użytkownika

Narzędzia witryny


xerces_dom

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:

Oraz zawiera częściową implementację (opartą o W3C Working Draft 09 April 2002) :


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ę 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 <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/dom/DOM.hpp> 
#include <xercesc/parsers/XercesDOMParser.hpp>

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 <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/dom/DOM.hpp> 
#include <xercesc/parsers/XercesDOMParser.hpp>
// ...
#include <iostream>  
 
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 <xercesc/sax/HandlerBase.hpp>
 
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:

<?xml version="1.0"?>
<!DOCTYPE notes SYSTEM "note.dtd">
<notes owner="John Doe">
   <note date="21/02/2008">
      <from>Jani</from>
      <heading>Reminder</heading>
      <body>Don't forget me this weekend</body>
   </note>
   <note date="12/04/2008">
      <from>Jeff</from>
      <heading>Money</heading>
      <body>Gimme my money back!</body>
   </note>
</notes>

DTD - notes.dtd

 <!ELEMENT notes (note+) > 
 <!ATTLIST notes owner CDATA #REQUIRED>
  <!ELEMENT note (from,heading,body)>
  <!ATTLIST note date CDATA #REQUIRED>
  <!ELEMENT from    (#PCDATA)>
  <!ELEMENT heading (#PCDATA)>
  <!ELEMENT body    (#PCDATA)>

Klasy pomocnicze support.h:

#include <string>
#include <iostream>
#include <stdexcept>
#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 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: " <<e.getLineNumber() << std::endl;
	}
	void resetErrors() {}
};

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<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()));	
		}	
	}
};

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;
}
xerces_dom.txt · ostatnio zmienione: 2008/04/16 15:33 przez kmioduszewski