[ C++ ] Parser & Writer XML avec Xerces-C++ et DOM
Par Deimos le 11-11-2005
parser du XML en C++ avec la puissante librarie Xerces-C++
Pour tous ceux qui essayeront de parser du XML en C++
avec la puissante librarie Xerces-C++, afin qu'ils ne se suicident pas sur la
documentation anglaise et quelque peu confuse d'Apache, étant donné qu'aucun
article français n'existe
encore sur le parser DOM et Xerces-c++ ...
Introduction
Le XML ( Extensible Markup Language ) a été proposé par le W3C (World Wide Web
Consortium) en 1998. Il constitue
un langage très souple, ceci étant du à la possibilité de créer ses propres
balises et attributs. De plus,
le XML a de nombreux avantages comme la structure et la hiérarchie des données
qui peut être définie
de manière stricte dans des fichiers DTD ( Document Type Definition ).
Cette organisation des données en arborescence est à la fois utile
et efficace : elle permet de lister de manière typée des données mais également
une lecture facile de ces données
par un programme. Ainsi le XML peut être un langage de base pour d'autres
langages, comme le XHTML, que l'on
peut voir en tant que l'évolution du HTML.
Rappels relatifs au XML
Tout document XML doit obligatoirement commencé par la ligne :
| Code: |
| <?xml version="1.0" encoding="iso-8859-1" standalone="no"?> |
Les attributs "encoding" et "standalone" sont cependant facultatifs. "encoding"
permet de définir le jeu de caractères
( charset ) utilisé ( UTF-8, ISO-8859-1, etc ) et "standalone" permet de définir
si le fichier .xml est accompagné d'un
fichier .dtd contenant les types des données de la structure XML.
On peut définir ces types soit dans un fichier.dtd à part, avec dans .xml ... :
| Code: |
<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?> <!DOCTYPE arborescence SYSTEM "types.dtd"> <arborescence> <entity type = "file" name = "New Text Document.txt" size = "21 835 bytes" date_creation = "vendredi 28 octobre 2005 14:08:26" date_modif = "vendredi 28 octobre 2005 14:37:39" date_access = "vendredi 4 novembre 2005 18:53:41" > </entity> </arborescence> |
et dans le .dtd ... :
| Code: |
<!ELEMENT entity (#PCDATA) > <!ATTLIST entity type CDATA #REQUIRED > <!ATTLIST entity name CDATA #REQUIRED > <!ATTLIST entity size CDATA #IMPLIED > <!ATTLIST entity date_creation CDATA #IMPLIED > <!ATTLIST entity date_modif CDATA #IMPLIED > <!ATTLIST entity date_access CDATA #IMPLIED > |
... soit en déclarant les types de données dans le .xml lui-même :
| Code: |
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes" ?> <!DOCTYPE arborescence [ <!ELEMENT entity (#PCDATA) > <!ATTLIST entity type CDATA #REQUIRED > <!ATTLIST entity name CDATA #REQUIRED > <!ATTLIST entity size CDATA #IMPLIED > <!ATTLIST entity date_creation CDATA #IMPLIED > <!ATTLIST entity date_modif CDATA #IMPLIED > <!ATTLIST entity date_access CDATA #IMPLIED > ] > <arborescence> <entity type = "file" name = "New Text Document.txt" size = "21 835 bytes" date_creation = "vendredi 28 octobre 2005 14:08:26" date_modif = "vendredi 28 octobre 2005 14:37:39" date_access = "vendredi 4 novembre 2005 18:53:41" > </entity> </arborescence> |
Le code XML lui-même n'est pas complexe : on créé un noeud principal, ici
"arborescence", suivi d'autres balises que
l'utilisateur définit ( ici "entity" ), et des attributs peuvent être également
déclarés ( type, name, etc ).
Pour le DTD, on déclare :
- un élément avec <!ELEMENT nom (type) >
- un atribut avec <!ATTLIST nom_element nom_attribute type options >
Il existe de nombreux types, ici j'utilise #PCDATE pour pouvoir inclure la
quasi-totalité des caractères. Pour une liste
exhaustive des types de données en XML, reportez vous à un tutorial entièrement
consacré au XML.
En ce qui concerne les options des attributs, #REQUIRED marque l'attribut comme
obligatoire et #IMPLIED comme facultatif.
A nouveau, il en existe d'autres, que vous trouverez dans de la documentation
relative a XML.
Passons aux choses sérieuses en C++
Coder un Parser DOM avec Xerces-c++
Xerces-c++ est une librairie portée en C++ permettant d'écrire et de parser des
documents XML. Cette librarire, actuellementà sa version 2.7.0 est développée
par Apache. Site officiel :
http://xml.apache.org/xerces-c/.
Vous y trouverez la liste des classes / méthodes et quelques exemples, mais rien
de très détaillé.
Xerces-c++ a mis au point deux parsers : SAX ( Simple API for XML ) basé sur un
mode évènementiel et DOM ( Document Object Model ) basé sur la hiérarchie des
éléments du document XML. DOM permet ainsi une navigation plus simplifié dans le
document XML et ceci est la raison pour laquelle j'ai choisi ce dernier pour
parser et écrire des données XML.
Nous allons parser le document XML suivant avec Xerces-C++ et le parser DOM ( il
représente l'arborescence d'un dossier de disque dur, qu'importe les éléments et
les attributs, ceci est uniquement pour l'exemple
:
| Code: |
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes" ?> <!DOCTYPE arborescence [ <!ELEMENT entity (entity) > <!ATTLIST entity type CDATA #REQUIRED > <!ATTLIST entity name CDATA #REQUIRED > <!ATTLIST entity size CDATA #IMPLIED > <!ATTLIST entity date_creation CDATA #IMPLIED > <!ATTLIST entity date_modif CDATA #IMPLIED > <!ATTLIST entity date_access CDATA #IMPLIED > <!ELEMENT entity (#PCDATA) > ] > <arborescence> <entity type = "file" name = "New Text Document.txt" size = "21 835 bytes" date_creation = "vendredi 28 octobre 2005 14:08:26" date_modif = "vendredi 28 octobre 2005 14:37:39" date_access = "vendredi 4 novembre 2005 18:53:41" > </entity> <entity type = "directory" name = "EasyPHP1-8" size = "97 902 592 bytes" date_creation = "dimanche 9 octobre 2005 00:29:44" > <entity type = "file" name = "EasyPHP.log" size = "4 096 bytes" date_creation = "mercredi 2 novembre 2005 19:56:39" date_modif = "mercredi 2 novembre 2005 19:56:18" date_access = "dimanche 6 novembre 2005 13:36:50" > </entity> <entity type = "file" name = "EasyPHP.exe" size = "172 032 bytes" date_creation = "mercredi 2 novembre 2005 19:56:39" date_modif = "jeudi 3 novembre 2005 19:30:10" date_access = "dimanche 6 novembre 2005 13:36:18" > </entity> <entity type = "directory" name = "mysql" size = "30 064 640 bytes" date_creation = "mercredi 2 novembre 2005 19:56:42" > <entity type = "file" name = "my-large" size = "4 096 bytes" date_creation = "mercredi 2 novembre 2005 19:56:42" date_modif = "mardi 9 septembre 2003 13:07:32" date_access = "dimanche 6 novembre 2005 13:46:22" > </entity> </entity> </entity> </arborescence> |
Voici un bref descriptif des méthodes utilisées dans le code du parser.
Cependant, attention : de nombreuses méthodes sont présentes dans plusieurs
classes différents et prennent donc des arguments différents, etc. Vous devez
donc toujours vérifier les arguments et ce que renvoi une méthode avec la
documentation :
http://xml.apache.org/xerces-c/apiDocs/functions.html
XMLPlatformUtils::Initialize()
Indispensable pour pouvoir utiliser un des parsers ( DOM ou SAX ) de Xerces-c++
: cette méthode initialise Xerces-C++.
Afin de l'utiliser, il est obligatoire d'inclure <src/xercesc/util/PlatformUtils.hpp>
sous Linux, ou juste
<xercesc/util/PlatformUtils.hpp> sous Windows, où xercesc est le répertoire ou
Xerces-c++ est décompressée.
XMLPlatformUtils::Terminate()
Cette méthode doit être la dernière appelée afin de marqué l'arrête de
l'utilisation de Xerces-C++.
XMLString::transcode(const char *const)
Permet de transcrire un string en caractère compatible avec les parsers (XMLCh
*).
La chaîne retournée est alloué dynamiquement, ainsi, pour libérer cet espace
mémoire, il est important d'appeler la méthode
XMLString::release() après avoir appelé transcode().
XercesDOMParser::XercesDOMParser(...)
Constructeur de la classe XercesDOMParser. Dans notre exemmple, nous utilisons
un parser XercesDOMParser, cependant nous pouvons tout à fait parser notre
fichier avec DOMBuilder. L'utilisation de certaines méthodes diffèrent selon de
la classe
utilisée.
XercesDOMParser::setErrorHandler(ErrorHandler *const
handler)
Permet de lier un handle d'erreur ErrorHandler au parser.
AbstractDOMParser::parse(const char *const systemId)
Permet de parser le fichier XML donné en paramètre.
XMLPlatformUtils::getCurrentMillis()
Renvoie le temps système en milliseconde. Cette fonction permet, avec une simple
soustraction, d'obtenir le temps qu'a mis
le programme pour parser le fichier XML, ou effectuer toute autre action.
DOMNode::getNodeType()
Renvoie le type de noeud (
[http://xml.apache.org/xerces-c/apiDocs/classDOMNode.html#z227_0 pour tous
les types de noeud )
DOMNode::getNodeName()
Renvoie le nom d'un noeud
DOMNode::hasAttributes()
Retourne true si le noeud à un ou des attributs, sinon false.
DOMNode::getAttributes()
Renvoie un pointeur DOMNamedNodeMap contenant la liste des attributs du noeud.
DOMNamedNodeMap::getNamedItem(const XMLCh *name)
Renvoie un pointeur DOMNode/DOMAttr vers le noeud/attribut spécifié.
DOMAttr::getValue()
Renvoie la valeur d'un attribut (cette fonction, liée aux deux précédentes
permet d'obtenir rapidement la valeur d'un attribut
d'un élément).
A présent, vous pouvez sans problème comprendre le code suivant
parser.cpp
| Code: |
#include <xercesc/util/PlatformUtils.hpp> #include <xercesc/util/XMLString.hpp> #include <xercesc/parsers/AbstractDOMParser.hpp> #include <xercesc/dom/DOM.hpp> #include <xercesc/parsers/XercesDOMParser.hpp> #include <string> #include <iostream> #include "ErrReporter.hpp" XERCES_CPP_NAMESPACE_USE using namespace std; // Fonction permettant de concaténer X fois // une chaine str string strcatX(string str,unsigned int x) { for(unsigned int y = 0; y <= x; y++) str += str; return str; } // Fonction displayElements : // fonction récursive qui affiche tous les noeuds ayant pour valeur // "directory" ou "file" en attribut "type" static int displayElements(DOMNode *n, unsigned int nbr_child) { DOMNode *child; // Compte les éléments unsigned int count = 0; // Compte le nombre d'appels à la fonction (récursive) // afin de pouvoir établir une tabulation au texte de sortie // de l'exécutable. Un appel = un répertoire static unsigned count_call = 0; // Rajoute un nombre nbr_child de tabulations afin de faciliter // la lisibilité de l'arborescence string xTab = strcatX(" ",nbr_child); if(n) { if(n->getNodeType() == DOMNode::ELEMENT_NODE) { if(n->hasAttributes()) { // Initialise un pointeur DOMNamedNodeMap sur les attributs du noeud courant DOMNamedNodeMap *pAttribus = n->getAttributes(); // Initialise un pointeur DOMNode sur l'attribut "type" du noeud XMLCh *nameAttr = XMLString::transcode("type"); DOMAttr *attr = (DOMAttr *) pAttribus->getNamedItem(nameAttr); XMLString::release(&nameAttr); // Récupère la valeur de l'attribut "type" char *Type = XMLString::transcode(attr->getValue()); if(!(strncmp(Type,"directory",10))) { // Récupère la valeur de l'attribut "name" pour afficher // le nom du répertoire nameAttr = XMLString::transcode("name"); attr = (DOMAttr *) pAttribus->getNamedItem(nameAttr); XMLString::release(&nameAttr); char *value = XMLString::transcode(attr->getValue()); cout << xTab << "----------------------\n" << endl; cout << xTab << "<DIR> " << value << "\n" << endl; XMLString::release(&value); // Appel la fonction afin de lister les fichiers du répertoire courant ++count_call; for (child = n->getFirstChild(); child != 0; child=child->getNextSibling()) count += displayElements(child,count_call); --count_call; ++count; } else if(!(strncmp(Type,"file",10))) { // Récupère la valeur de l'attribut "name" pour afficher // le nom du fichier nameAttr = XMLString::transcode("name"); attr = (DOMAttr *) pAttribus->getNamedItem(nameAttr); XMLString::release(&nameAttr); char *value = XMLString::transcode(attr->getValue()); cout << xTab << "<FILE> " << value << "\n" << endl; XMLString::release(&value); ++count; } XMLString::release(&Type); } else { for (child = n->getFirstChild(); child != 0; child=child->getNextSibling()) count += displayElements(child,count_call); } } } return count; } // Fonction Main int main(int argC, char* argV[]) { bool erreur = false; // Vérifie le bon nombre d'arguments if(argC < 2) { cout << "Usage : " << argV[0] << " filetoparse.xml" << endl; return 1; } // Initialisation du parser try { XMLPlatformUtils::Initialize(); } catch (const XMLException& err) { cerr << "Erreur pendant l'initialisation de Xerces-C++ :\n" << XMLString::transcode(err.getMessage()) << endl; return 2; } // Faire en sorte que le parser DOM renvoie tous les codes d'erreurs AbstractDOMParser::ValSchemes valScheme = AbstractDOMParser::Val_Auto; // Instancie le parser DOM XercesDOMParser *parser = new XercesDOMParser; // Instancie un ErrorHandler et le lie au parser ErrReporter *errHandler = new ErrReporter(); parser->setErrorHandler(errHandler); DOMDocument *doc; unsigned int duration; // Parse le fichier XML et récupère le temps mis pour le parsing try { parser->resetDocumentPool(); const unsigned long startMillis = XMLPlatformUtils::getCurrentMillis(); parser->parse(argV[1]); doc = parser->getDocument(); cout << "======== Parsing " << argV[1] << " ========\n" << endl; const unsigned long endMillis = XMLPlatformUtils::getCurrentMillis(); duration = endMillis - startMillis; } // Gestion des exceptions possibles pendant le parsing // Exception XML catch (const XMLException& err) { cerr << "Erreur XML pendant le parsing du fichier : " << argV[1] << "\n" << "Exception XML : \n" << XMLString::transcode(err.getMessage()) << "\n" << endl; erreur = true; } // Exception DOM catch (const DOMException& err) { const unsigned int maxChars = 2047; XMLCh errText[maxChars + 1]; cerr << "Erreur DOM pendant le parsing du fichier : " << argV[1] << "\n" << "Code d'erreur DOM : " << err.code << "\n" << endl; if (DOMImplementation::loadDOMExceptionMsg(err.code, errText, maxChars)) cerr << "Exception DOM : " << XMLString::transcode(errText) << "\n" << endl; erreur = true; } // Autre exception catch (...) { cerr << "Erreur inattendue durant le parsing du fichier : " << argV[1] << "\n" << endl; erreur = true; } unsigned int compteur; if(doc && !errHandler->fSawErrors) { // Affiche les éléments du fichier XML compteur = displayElements((DOMNode*)doc->getDocumentElement(),0); } // Affiche le nombre d'éléments du fichier XML cout << "Nombre d'élements du fichier " << argV[1] << " : " << compteur << "\n" << endl; // Affiche le temps mis pour le parsing cout << "Temps de parsing du fichier " << argV[1] << " : " << duration << " ms" << endl; // Supprime le handle d'erreur delete errHandler; // Supprime le parser delete parser; // Appel à la méthode Terminate XMLPlatformUtils::Terminate(); if(erreur == true) return 3; else return 0; } |
ErrReporter.cpp
| Code: |
#include <xercesc/sax/SAXParseException.hpp> #include <iostream> #include <string> #include "ErrReporter.hpp" using namespace std; void ErrReporter::warning(const SAXParseException&) { // Ignore les avertissements // On peut cependant très bien afficher les messages // d'avertissements en codant cette fonction // comme l'une des suivantes } void ErrReporter::error(const SAXParseException& mess) { fSawErrors = true; cerr << "Erreur dans le fichier \"" << XMLString::transcode(mess.getSystemId()) << "\", ligne " << mess.getLineNumber() << ", colonne " << mess.getColumnNumber() << "\nMessage d'erreur : " << XMLString::transcode(mess.getMessage()) << endl; } void ErrReporter::fatalError(const SAXParseException& mess) { fSawErrors = true; cerr << "Erreur fatale dans le fichier \"" << XMLString::transcode(mess.getSystemId()) << "\", ligne " << mess.getLineNumber() << ", colonne " << mess.getColumnNumber() << "\nMessage d'erreur : " << XMLString::transcode(mess.getMessage()) << endl; } void ErrReporter::resetErrors() { fSawErrors = false; } |
ErrReporter.hpp
| Code: |
#include <xercesc/util/XercesDefs.hpp> #include <xercesc/sax/ErrorHandler.hpp> #include <iostream> XERCES_CPP_NAMESPACE_USE class ErrReporter : public ErrorHandler { public: ErrReporter() : fSawErrors(false) { } ~ErrReporter() { } // Méthodes d'implémentation de ErrorHandler void warning(const SAXParseException& mess); void error(const SAXParseException& mess); void fatalError(const SAXParseException& mess); void resetErrors(); // Méthode Get bool getSawErrors() const; // Donnée privée permettant de savoir si des erreurs // ont eu lieu avec getSawErrors() et de remettre le // compteur d'erreurs à zéro bool fSawErrors; }; inline bool ErrReporter::getSawErrors() const { return fSawErrors; } |
En exécutant le programme, on obtient à l'écran l'arborescence complète du
fichier XML :
| Code: |
======== Parsing fichier.xml ======== <FILE> New Text Document.txt ---------------------- <DIR> EasyPHP1-8 <FILE> EasyPHP.log <FILE> EasyPHP.exe ---------------------- <DIR> mysql <FILE> my-large Nombre d'Úlements du fichier fichier.xml : 6 Temps de parsing du fichier fichier.xml : 15 ms |
A présent le parser DOM codé, attaquons-nous au DOMWriter !
Créer ses documents XML en codant un
DOMWriter
Avoir le pouvoir d'écrire des données XML facilement est également un atout
qu'apporter la librairie Xerces-C++. Afin de
présenter ces fonctionnalités, j'ai codé un petit exemple regroupant l'essentiel
: création d'un document XML, avec un
noeud racine, un élément avec un attribut et une valeur.
Ainsi il suffira de reproduire plusieurs fois ce qui est fait dans ce code (
coder des fonctions, etc ) pour créer
tout un fichier XML avec des centaines d'éléments imbriqués, des attributs, etc.
Descriptif des méthodes utilisées pour le DOMWriter (en supplément des méthodes
utilisés pour le parser) :
DOMImplementation::createDocument(...)
Créé un document XML vide et renvoie un pointeur DOMDocument* vers ce document.
DOMDocument::getDocumentElement()
Renvoie un pointeur DOMElement* vers l'élément racine du document.
DOMDocument::createElement(const XMLCh* tagName)
Créé un élément ayant le nom tagName dans le document XML et renvoie un pointeur
DOMElement* vers celui-ci.
DOMDocument::createAttribute(const XMLCh* name)
Créé un attribut ayant le nom name et renvoie un pointeur DOMAttr vers-celui-ci.
Pour que l'attribut ai une valeur, il faut
utiliser DOMAttr::setValue et pour le lier à un élément, utiliser DOMElement::setAttributeNode()
DOMAttr::setValue(const XMLCh* value)
Paramètre la valeur de l'attribut à value.
DOMElement::setAttributeNode(DOMAttr* newAttr)
Rajoute/lie un attribut à un élément du document XML ( DOMElement* ).
DOMDocument::createTextNode(const XMLCh* data)
Ecrit une valeur et la renvoie dans un pointeur DOMText*. On peut lier cette
valeur à un élément avec DOMNode::appendChild()
DOMNode::appendChild(DOMNode* newchild)
Rajoute/lie une valeur ( DOMText* ) à un noeud XML.
XMLUni::fgDOMWRTFormatPrettyPrint
Permet d'avoir en sortie des données XML lisibles facilement par un être humain
( tabulations, retours chariot, etc )
LocalFileFormatTarget::LocalFileFormatTarget(...)
Constructeur de la classe LocalFileFormatTarget, il permet de spécifier le
chemin du fichier XML dans lequel on enregistre
le DOMDocument* et renvoie un pointeur XMLFormatTarget* contenant ce chemin. On
peut utiliser StdOutFormatTarget::StdOutFormatTarget() afin d'afficher le
document XML à l'écran au lieu de l'enregistrer dans un fichier.
DOMWriter::writeNode(XMLFormatTarget *const
destination, const DOMNode &nodeToWrite)
Enregistre le document XML au chemin destination.
DOMWriter.cpp
| Code: |
#include <xercesc/util/PlatformUtils.hpp> #include <xercesc/util/XMLString.hpp> #include <xercesc/parsers/AbstractDOMParser.hpp> #include <xercesc/dom/DOM.hpp> #include <xercesc/parsers/XercesDOMParser.hpp> #include <xercesc/framework/LocalFileFormatTarget.hpp> #include <xercesc/framework/StdOutFormatTarget.hpp> #include <string> #include <iostream> XERCES_CPP_NAMESPACE_USE using namespace std; int main(int argC, char* argV[]) { bool erreur = false; // Vérifie le bon nombre d'arguments if(argC < 2) { cout << "Usage : " << argV[0] << " file.xml" << endl; return 1; } // Initialisation du parser try { XMLPlatformUtils::Initialize(); } catch (const XMLException& err) { cerr << "Erreur pendant l'initialisation de Xerces-c++ :\n" << XMLString::transcode(err.getMessage()) << endl; return 2; } XMLCh* value = XMLString::transcode("LS"); DOMImplementation* dom_impl = DOMImplementationRegistry::getDOMImplementation(value); XMLString::release(&value); DOMDocument* doc; try { // Création du document XML vide avec comme // noeud racine "arborescence" value = XMLString::transcode("arborescence"); doc = dom_impl->createDocument(NULL,value,NULL); XMLString::release(&value); } catch(const DOMException& err) { cerr << "Erreur pendant la creation du document XML ( DOM Exception ) :\n" << XMLString::transcode(err.getMessage()) << endl; return 3; } // Récupère un pointeur DOMElement vers le noeud racine DOMElement* root = doc->getDocumentElement(); DOMElement* elementDir; DOMAttr* attrDir; DOMText* File; try { // Créer un élément value = XMLString::transcode("directory"); elementDir = doc->createElement(value); XMLString::release(&value); // Créer un attribut value = XMLString::transcode("type"); attrDir = doc->createAttribute(value); XMLString::release(&value); // Configuration de la valeur de l'attribut value = XMLString::transcode("directory"); attrDir->setValue(value); XMLString::release(&value); // Créer une valeur texte value = XMLString::transcode("php.exe"); File = doc->createTextNode(value); XMLString::release(&value); } catch(const DOMException& err) { cerr << "Erreur pendant la creation des noeuds ( DOM Exception ) :\n" << XMLString::transcode(err.getMessage()) << endl; return 3; } try { // Lie la valeur au noeud elementDir->appendChild(File); // Lieu l'attribut au noeud elementDir->setAttributeNode(attrDir); // Lie le noeud à la racine du document root->appendChild(elementDir); } catch(const DOMException& err) { cerr << "Erreur pendant la création ddes liens entre les noeuds ( DOM Exception ) :\n" << XMLString::transcode(err.getMessage()) << endl; return 3; } DOMWriter *domWriter = ((DOMImplementationLS*)dom_impl)->createDOMWriter(); if (domWriter->canSetFeature(XMLUni::fgDOMWRTSplitCdataSections, true)) domWriter->setFeature(XMLUni::fgDOMWRTSplitCdataSections, true); if (domWriter->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true)) domWriter->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true); value = XMLString::transcode(argV[1]); // OU XMLFormatTarget *fileTarget = new StdOutFormatTarget(); XMLFormatTarget *fileTarget = new LocalFileFormatTarget(value); XMLString::release(&value); domWriter->writeNode(fileTarget, *doc); delete fileTarget; delete domWriter; delete doc; XMLPlatformUtils::Terminate(); return 0; } |
Nous obtenons comme fichier XML :
| Code: |
<?xml version="1.0" encoding="UTF-8" standalone="no" ?> <arborescence> <directory type="php">php.exe</directory> </arborescence> |
Voilà, on a à présent deux programmes fonctionnels permettant de créer & parser
des données XML ... Il ne reste plus qu'à mettre tout ça en pratique pour vos
futurs exécutables utilisant XML, comme base de données, ou autre.
Bon coding,
Deimos