- Libraitrie MinXL : Minimalist XML *
Il s'agit d'une librairie codée en C++ et basée sur la STL uniquement. Le code est donc portable. Il a été compilé sous Windows2000/MinGW/DevCpp, GNU/linux/Eclipse/CDT et sous WindowsXP/Cygwin/Eclipse/CDT avec succès.
Le source de cette librairie se compose de deux fichiers autonomes : xml.hpp (en-tête C++) et xml.cpp (source C++). Un fichier supplémentaire "xml_test.cpp" est un programme principale de test (25 tests successifs + 2 exemples commentés). Ce dernier donne différents cas d'utilisation de la librairie MinXL, en plus de vérifier son bon fonctionnement point par point. Les sources sont documentés en anglais (pauvre mais je l'espère lisible par tous).
---
Cette librairie contient deux classes regroupées au sein du namespace MinXL :
- t_xml_node : classe élémentaire offrant une abstraction d'un noeud/arbre XML (formé d'une chaine 'clé', de chaines 'attributs' et d'une chaine 'valeur'),
- t_xml_manip : classe facilitant le traitement d'un arbre XML complexe offrant une interface proche d'une invite de commande DOS/Linux (on parcours l'arbre XML comme un système de fichiers par des commandes/méthodes du genre 'list', 'make', 'remove', 'copy', 'move', 'read', 'write', etc...).
---
Limitation : le 'parser' ignore seulement le prologue (ex.: <?xxxx?>) et les commentaires (ex.: <!-- xxx -->), sans les décoder. Les performances du 'parser' sont également tres pauvres (constaté par yAAm). Une optimisation du code est en cours sans nuire à sa lisibilité (gain de rapidité de l'ordre de 500% actuellement, par rapport à la première release).
---
Toutes remarques sont les bienvenues. Je suis prêt à modifier le code source pour prendre en charge de nouvelles fonctionnalités suggérées par un éventuel utilisateur intéressé. Merci.
---
Ce projet est disponible sur SourceForge.Net : Unix Name= "minxl". CVS à jour.
---
Cordialement,
Xterm-in-Hate.
Source / Exemple :
/////////////////////////////////////////////////////////////////////////////
// CECI EST UN APPERCU DU CODE ZIPPE....
/////////////////////////////////////////////////////////////////////////////
struct t_xml_node
{
// constructor
t_xml_node( std::string const & key = "_EMPTY_", std::string const & value = "" );
// destructor
virtual ~t_xml_node();
// copy constructor
t_xml_node( t_xml_node const & node );
// assignment
t_xml_node & operator=( t_xml_node const & node );
// key accessers
std::string const & key() const;
std::string & key();
// value accessers
std::string const & value() const;
std::string & value();
// number of attributes
std::size_t nb_attributes() const;
// add an attributes
void insert_attribute( std::string const & attribute, std::string const & value );
// remove an attributes
void remove_attribute( std::size_t const & index );
// remove an attribute through string indexer
void remove_attribute( std::string const & attribute );
// get an attribute
std::string const & attribute_at( std::size_t const & index ) const;
std::string & attribute_at( std::size_t const & index );
// get an attribute associated value
std::string const & attribute_value_at( std::size_t const & index ) const;
std::string & attribute_value_at( std::size_t const & index );
// get an attribute associated through string indexer (key_suffix = "aaaa:n", "aaaa"+":0" by default suffix)
std::string const & attribute_value_at( std::string const & attribute ) const;
std::string & attribute_value_at( std::string const & attribute );
// number of children
std::size_t nb_children() const;
// add a child
void insert_child( t_xml_node const & child );
// remove a child
void remove_child( std::size_t const & index );
// remove a child through string indexer (key_suffix = "aaaa:n", "aaaa"+":0" by default suffix)
void remove_child( std::string const & key_suffix );
// get a child
t_xml_node const & child_at( std::size_t const & index ) const;
t_xml_node & child_at( std::size_t const & index );
// get a child through indexer (key_suffix = "aaaa:n", "aaaa"+":0" by default suffix)
t_xml_node const & child_at( std::string const & key_suffix ) const;
t_xml_node & child_at( std::string const & key_suffix );
// build : transform a tree/node into a XML stream/file
void build_to_file( std::string const & path_filename ) const;
std::string build_to_str() const;
// parse : transform a XML stream/file into a tree/node
void parse_from_file( std::string const & path_filename );
void parse_from_str( std::string const & str );
};
/////////////////////////////////////////////////////////////////////////////
// CECI EST UN APPERCU DU CODE ZIPPE....
/////////////////////////////////////////////////////////////////////////////
struct t_xml_manip
{
// constructor
t_xml_manip( t_xml_node & root );
// key management
std::vector< std::string > ls( std::string const & path ) const; // list keys
void mk( std::string const & path ) const; // make new key or new attribute
void rm( std::string const & path ) const; // remove key or attribute
void mv( std::string const & src_path, std::string const & dst_path ) const; // move key
void cp( std::string const & src_path, std::string const & dst_path ) const; // copy key
// key-value and attribute-value read/write
std::string const & operator()( std::string const & path ) const; // read key-value or attribute-value
std::string & operator()( std::string const & path ); // read/write key-value or attribute-value
std::string const & read( std::string const & path ) const; // read key-value or attribute-value
void write( std::string const & value, std::string const & path ) const; // write key-value or attribute-value
}
/////////////////////////////////////////////////////////////////////////////
// CECI EST UN APPERCU DU CODE ZIPPE....
/////////////////////////////////////////////////////////////////////////////
{
// example 1.1.1 :
//
// player_list (number_of_players : 2)
// +---> player (id : 0001)
// | +---> name : xterm-in-hate
// | +---> level : 39
// | +---> class : berseker
// | +---> experience : 8012
// | +---> position
// | | +---> world : earth
// | | +---> xcord : 111
// | | +---> ycord : 452
// | | +---> zcord : 37
// | | +---> xyangle : 162
// | | +---> xzangle : -9
// | +---> health : 120
// | +---> mana : 270
// +---> player (id : 0002)
// . +---> name : guest
// . +---> level : 1
// . +---> class : paladin
// . +---> experience : 0
// . +---> position
// . | +---> world : earth
// . | +---> xcord : 118
// . | +---> ycord : 493
// . | +---> zcord : 38
// . | +---> xyangle : 25
// . | +---> xzangle : 0
// . +---> health : 50
// . +---> mana : 22
cout << "EXAMPLE#1.1.1" << endl;
// root node : player list
t_xml_node root( "player_list", "" );
root.insert_attribute("number_of_players","2");
// child 1 node : player
t_xml_node c1( "player", "" );
c1.insert_attribute( "id", "0001" );
// child 1-1 node : name
t_xml_node c11( "name", "xterm-in-hate" );
c1.insert_child(c11);
// child 1-2 node : level
t_xml_node c12( "level", "39" );
c1.insert_child(c12);
// child 1-3 node : class
t_xml_node c13( "class", "berseker" );
c1.insert_child(c13);
// child 1-4 node : experience
t_xml_node c14( "experience", "8012" );
c1.insert_child(c14);
// child 1-5 node : position
t_xml_node c15( "position", "" );
// child 1-5-1 node : world
t_xml_node c151( "world", "earth" );
c15.insert_child(c151);
// child 1-5-2 node : xcord
t_xml_node c152( "xcord", "111" );
c15.insert_child(c152);
// child 1-5-3 node : ycord
t_xml_node c153( "ycord", "452" );
c15.insert_child(c153);
// child 1-5-4 node : zcord
t_xml_node c154( "zcord", "37" );
c15.insert_child(c154);
// child 1-5-5 node : xyangle
t_xml_node c155( "xyangle", "162" );
c15.insert_child(c155);
// child 1-5-6 node : xzangle
t_xml_node c156( "xzangle", "-9" );
c15.insert_child(c156);
// child 1-5 node : position (end)
c1.insert_child(c15);
// child 1-6 node : health
t_xml_node c16( "health", "120" );
c1.insert_child(c16);
// child 1-7 node : mana
t_xml_node c17( "mana", "270" );
c1.insert_child(c17);
// child 1 node : player (end)
root.insert_child(c1);
// child 2 node : player
t_xml_node c2( "player", "" );
c2.insert_attribute( "id", "0002" );
// child 2-1 node : name
t_xml_node c21( "name", "guest" );
c2.insert_child(c21);
// child 2-2 node : level
t_xml_node c22( "level", "1" );
c2.insert_child(c22);
// child 2-3 node : class
t_xml_node c23( "class", "paladin" );
c2.insert_child(c23);
// child 2-4 node : experience
t_xml_node c24( "experience", "0" );
c2.insert_child(c24);
// child 2-5 node : position
t_xml_node c25( "position", "" );
// child 2-5-1 node : world
t_xml_node c251( "world", "earth" );
c25.insert_child(c251);
// child 2-5-2 node : xcord
t_xml_node c252( "xcord", "118" );
c25.insert_child(c252);
// child 2-5-3 node : ycord
t_xml_node c253( "ycord", "493" );
c25.insert_child(c253);
// child 2-5-4 node : zcord
t_xml_node c254( "zcord", "38" );
c25.insert_child(c254);
// child 2-5-5 node : xyangle
t_xml_node c255( "xyangle", "25" );
c25.insert_child(c255);
// child 2-5-6 node : xzangle
t_xml_node c256( "xzangle", "0" );
c25.insert_child(c256);
// child 2-5 node : position (end)
c2.insert_child(c25);
// child 2-6 node : health
t_xml_node c26( "health", "50" );
c2.insert_child(c26);
// child 2-7 node : mana
t_xml_node c27( "mana", "22" );
c2.insert_child(c27);
// child 2 node : player (end)
root.insert_child(c2);
// build XML file/stream
cout << root.build() << endl;
}
{
// example 2.1.1 :
//
// player_list (number_of_players : 2)
// +---> player (id : 0001)
// | +---> name : xterm-in-hate
// | +---> level : 39
// | +---> class : berseker
// | +---> experience : 8012
// | +---> position
// | | +---> world : earth
// | | +---> xcord : 111
// | | +---> ycord : 452
// | | +---> zcord : 37
// | | +---> xyangle : 162
// | | +---> xzangle : -9
// | +---> health : 120
// | +---> mana : 270
// +---> player (id : 0002)
// . +---> name : guest
// . +---> level : 1
// . +---> class : paladin
// . +---> experience : 0
// . +---> position
// . | +---> world : earth
// . | +---> xcord : 118
// . | +---> ycord : 493
// . | +---> zcord : 38
// . | +---> xyangle : 25
// . | +---> xzangle : 0
// . +---> health : 50
// . +---> mana : 22
cout << "EXAMPLE#2.1.1" << endl;
// root node : player list
t_xml_node root( "player_list", "" );
t_xml_manip m( root );
// root node : player list
m.mk("/player_list/.number_of_players");
m("/player_list/.number_of_players") = "2";
// player 1
m.mk("/player_list/player");
m.mk("/player_list/player:0/.id");
m("/player_list/player:0/.id") = "0001";
// player 1 name
m.mk("/player_list/player:0/name");
m("/player_list/player:0/name") = "xterm-in-hate";
// player 1 level
m.mk("/player_list/player:0/level");
m("/player_list/player:0/level") = "39";
// player 1 class
m.mk("/player_list/player:0/class");
m("/player_list/player:0/class") = "berseker";
// player 1 experience
m.mk("/player_list/player:0/experience");
m("/player_list/player:0/experience") = "8012";
// player 1 position
m.mk("/player_list/player:0/position");
m.mk("/player_list/player:0/position/world");
m("/player_list/player:0/position/world") = "earth";
m.mk("/player_list/player:0/position/xcord");
m("/player_list/player:0/position/xcord") = "111";
m.mk("/player_list/player:0/position/ycord");
m("/player_list/player:0/position/ycord") = "452";
m.mk("/player_list/player:0/position/zcord");
m("/player_list/player:0/position/zcord") = "37";
m.mk("/player_list/player:0/position/xyangle");
m("/player_list/player:0/position/xyangle") = "162";
m.mk("/player_list/player:0/position/xzangle");
m("/player_list/player:0/position/xzangle") = "-9";
// player 1 health
m.mk("/player_list/player:0/health");
m("/player_list/player:0/health") = "120";
// player 1 mana
m.mk("/player_list/player:0/mana");
m("/player_list/player:0/mana") = "270";
// player 2
m.mk("/player_list/player");
m.mk("/player_list/player:1/.id");
m("/player_list/player:1/.id") = "0002";
// player 2 name
m.mk("/player_list/player:1/name");
m("/player_list/player:1/name") = "guest";
// player 2 level
m.mk("/player_list/player:1/level");
m("/player_list/player:1/level") = "1";
// player 2 class
m.mk("/player_list/player:1/class");
m("/player_list/player:1/class") = "paladin";
// player 2 experience
m.mk("/player_list/player:1/experience");
m("/player_list/player:1/experience") = "0";
// player 2 position
m.mk("/player_list/player:1/position");
m.mk("/player_list/player:1/position/world");
m("/player_list/player:1/position/world") = "earth";
m.mk("/player_list/player:1/position/xcord");
m("/player_list/player:1/position/xcord") = "118";
m.mk("/player_list/player:1/position/ycord");
m("/player_list/player:1/position/ycord") = "493";
m.mk("/player_list/player:1/position/zcord");
m("/player_list/player:1/position/zcord") = "38";
m.mk("/player_list/player:1/position/xyangle");
m("/player_list/player:1/position/xyangle") = "25";
m.mk("/player_list/player:1/position/xzangle");
m("/player_list/player:1/position/xzangle") = "0";
// player 2 health
m.mk("/player_list/player:1/health");
m("/player_list/player:1/health") = "50";
// player 2 mana
m.mk("/player_list/player:1/mana");
m("/player_list/player:1/mana") = "22";
// build XML file/stream
cout << root.build() << endl;
}
/////////////////////////////////////////////////////////////////////////////
// CECI EST UN APPERCU DU CODE ZIPPE....
/////////////////////////////////////////////////////////////////////////////
Conclusion :
Formalisme de t_xml_manip. Les noeuds/attributs XML sont manipulés comme un système de fichier. La notion de chemin est donc la clé de cette interface. Par exemple : "/racine/noeud" pointe vers le noeud 'noeud' appartenant à la racine 'racine'; "/racine/noeud/.attrib" pointe vers l'attribut 'attrib' appartenant au noeud 'noeud', appartenant à la racine 'racine'.
---
Je remercie Tom87@21 et BaBar60 qui m'ont soutenu pendant la première phase de développement de cette petite librairie. Merci également à l'ensemble des intervenant qui au travers de leur remarques et observation m'ont permis d'améliorer MinXL.
---
Je remercie aussi toutes les personnes qui, grace à leurs commentaires, ont permis à MinXL d'évoluer favorablement au fil du temps.
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.