qui permet l'échange de données structurées. De nombreux formats spécialisés, tels que SVG ou
HTML, correspondent à une utilisation particulière de XML. Les langages de programmation de
haut-niveau sont souvent dotés d'une bibliothèque d'import/export de données XML. Nous
proposons ici d'en développer une pour le langage C.
Voici un exemple de code XML :
<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author lastname="Gambardella" firstname="Matthew" />
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
<book id="bk102">
<author lastname="Ralls" firstname="Kim" />
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
</book>
...
</catalog>
Comme on peut le constater dans cet exemple, XML permet de définir des données
(quelconques, ici il s'agit d'une liste de livres mais cela aurait pu être n'importe quel autre type
de données) sous une forme arborescente. Ici, on décrit une liste de livres comme étant un
catalog dont chaque entrée est un book. Chaque book est lui-même décrit en précisant son
auteur, son titre, son genre, son prix, etc.
Les données sont décrites à travers des éléments qui s'imbriquent les uns dans les autres pour
constituer un arbre. Dans notre cas, un élément catalog contient deux éléments book, eux-
mêmes contenant des éléments author, title, etc. Chaque élément est structuré
syntaxiquement de la même façon :
une balise ( markup ) ouvrante données entre chevrons '<' et '>' et contenant :
le nom de l'élément ( tag ), suivi
d'une liste possiblement vide d'attributs,
soit une suite d'éléments XML,
soit une donnée textuelle,
une balise fermante entre chevrons '</' et '>' contenant juste le nom de l'élément
http://www.lacl.fr/~aspicher/Wiki/l2info/progc/xml 31/12/2017, 10?
Chaque attribut possède un nom et une valeur (donnée entre guillemets).
<nom_element nom_attribut1 = "valeur_attribut1" nom_attribut2 = "valeur_attribut2">
...
</nom_element>
Lorsque l'élément n'a pas de contenu (c'est par exemple le cas de l'élément author dans notre
exemple, les balises ouvrantes et fermantes sont souvent fusionnées en une seule balise. Ainsi
<author lastname="Gambardella" firstname="Matthew" />
<author lastname="Gambardella" firstname="Matthew"> </author>
Hormis les quelques règles syntaxiques que nous venons d'évoquer, le reste (le nom des balises,
des attributs, le contenu des valeurs) est laissé libre. Pour simplifier, nous ne nous
préoccuperons pas de la première balise spéciale .
On propose de définir le type des documents XML de la façon suivante. Un élément XML contient
un nom (chaîne de caractères),
une liste d'attributs (voir ci-dessous),
une référence (pointeur) vers son élément père s'il en contient un,
une référence (pointeur) vers son élément frère s'il en contient un, et
un contenu qui peut être :
soit une donnée brute (chaîne de caractères) ou raw en anglais,
soit une liste d'éléments fils dont il est le père.
Un attribut XML contient :
un nom (chaîne de caractères),
une valeur (chaîne de caractères) sans les guillemets, et
une référence vers l'attribut suivant
Types, constructeurs et destructeurs
Définir le type xattribute_t représentant un attribut XML tel que décrit ci-dessus
Définir le type xelement_t représentant un élément XML tel que décrit ci-dessus
Définir le constructeur xelement_t* create_xelement(const char* name)
qui crée un élément XML de nom name sans attribut ni père ni frère ni contenu ; on
prendra garde de copier le nom de l'élément
Définir la fonction xattribute_t* add_xattribute(xelement_t* e, const
http://www.lacl.fr/~aspicher/Wiki/l2info/progc/xml 31/12/2017, 10?
char* name, const char* value) qui ajoute à l'élément e un attribut de nom
name et de valeur value ; on prendra garde à copier le nom et la valeur de l'attribut ;
si l'élément possède déjà un attribut de ce nom, la précédente valeur sera écrasée (et la
Définir la fonction void add_sub_xelement(xelement_t* e, xelement_t*
s) qui ajoute l'élément s comme fils à l'élément e ; on vérifiera que s n'a pas de père et
que e est soit vide, soit contient déjà des sous-éléments ; dans le cas contraire, le
Définir la fonction void add_raw(xelement_t* e, const char* r) qui
ajoute une donnée brute r à l'élément e ; on vérifiera que e est soit vide, soit contient
déjà une donnée brute (auquel cas, celle-ci sera remplacée et la mémoire libérée) ; dans
le cas contraire, le programme échouera
Définir la fonction void delete_xelement(xelement_t* e) qui libère la
mémoire occupée par e ; on vérifiera que e n'a pas de père
Définir la fonction void save_xelement(FILE* fd, xelement_t* e) qui
sauvegarde l'élément e dans fd en utilisant la syntaxe XML
Définir la fonction void save_xml(const char* fname, xelement_t* e)
qui sauvegarde l'élément e dans le fichier nommé fname (qui sera écrasé le cas
Ajouter la fonction void print_xelement(xelement_t* e) définie par :
void print_xelement(xelement_t* e) {
save_xelement(stdout, e);
}
L'import d'un fichier XML est une fonctionnalité autrement plus difficile à programmer que
l'export. Elle demande en effet à parser un fichier texte. Voici une partie du code permettant cet
import à la condition de programmer correctement les fonctions spécifiées ci-dessous :
xelement_t* load_xelement(FILE* fd, const char* end_tag);
void load_xelement_raw(FILE* fd, xelement_t* e) {
char* w = next_raw(fd);
check_next_char(fd, '<');
check_next_char(fd, '/');
check_next_word(fd, e->name);
check_next_char(fd, '>');
add_raw(e,w);
free(w);
}
http://www.lacl.fr/~aspicher/Wiki/l2info/progc/xml 31/12/2017, 10?
void load_xelement_sub(FILE* fd, xelement_t* e) {
xelement_t* f = load_xelement(fd, e->name);
if (f != NULL) {
add_sub_xelement(e,f);
load_xelement_sub(fd, e);
}
}
void load_xelement_content(FILE* fd, xelement_t* e) {
if (is_next_char(fd,'<',false))
load_xelement_sub(fd, e);
else
load_xelement_raw(fd, e);
}
xelement_t* load_xelement(FILE* fd, const char* end_tag) {
xelement_t* e = NULL;
char c;
check_next_char(fd,'<');
if ((end_tag) && (is_next_char(fd,'/',true))) {
check_next_word(fd,end_tag);
check_next_char(fd,'>');
return NULL;
}
char* name = next_word(fd);
if (name == NULL) {
fprintf(stderr, "load_xelement: tag name expected\n");
exit(EXIT_FAILURE);
}
e = create_xelement(name);
free(name);
while((name = next_word(fd)) != NULL) {
check_next_char(fd,'=');
char* value = next_string(fd);
add_xattribute(e,name,value);
}
c = next_char(fd);
if (c == '/') {
check_next_char(fd,'>');
return e;
}
if (c == '>') {
load_xelement_content(fd, e);
return e;
}
fprintf(stderr, "load_xelement: end of markup expected ('>' or '/>'), but got %c\n", c);
exit(EXIT_FAILURE);
}
xelement_t* load_xml(const char* fname) {
FILE* fd = fopen(fname, "r");
xelement_t* e = load_xelement(fd,NULL);
fclose(fd);
return e;
}
Pour que ce code fonctionne, il est nécessaire de programmer les fonctions suivantes :
char next_char(FILE* fd) qui retourne le prochain caractère dans fd qui n'est
pas un caractère d'espacement (c'est-à-dire ni ' ', ni '\n', ni '\r', ni '\t') : le programme
échouera si la fin de fichier est rencontrée
http://www.lacl.fr/~aspicher/Wiki/l2info/progc/xml 31/12/2017, 10?
void check_next_char(FILE* fd, char c) qui fait échouer le programme si
le prochain caractère (next_char) n'est pas c
bool is_next_char(FILE* fd, char c, bool cons) qui vérifie si le
prochain caractère (next_char) est c ; dans le cas contraire ou si cons est à faux, le
prochain caractère ne sera pas consommé (utiliser ungetc)
char* next_word(FILE* fd) qui retourne le prochain identifiant (nom d'élément
ou d'attribut) dans fd ; les caractères d'espacement précédant le mot seront consommés
et la lecture du mot s'arrêtera dès qu'on rencontrera un caractère parmi '<', '>', '/', '=' et
les caractères d'échappement, qui ne sera pas consommé (ungetc) ; le programme
échouera si la fin de fichier est rencontrée pendant la lecture ; la chaîne retournée sera
allouée par la fonction (attention, il n'y a pas de limite de taille sur les identifiants)
void check_next_word(FILE* fd, const char* w) qui fait échouer le
programme si le prochain mot (next_word) n'est pas w (utiliser strcmp pour
char* next_string(FILE* fd) qui consomme les caractères d'espacement
jusqu'au prochain guillemet ” (échoue dans le cas contraire) et retourne l'ensemble des
caractères rencontrés jusqu'au guillemet suivant (qui sera consommé) ; la chaîne
retournée sera allouée par la fonction (attention, il n'y a pas de limite de taille sur les
char* next_raw(FILE* fd) qui retourne la suite des caractères jusqu'au prochain
< (qui ne sera pas consommé ; utiliser ungetc) ; la chaîne retournée sera allouée par
la fonction (attention, il n'y a pas de limite de taille sur les données brutes)
l2info/progc/xml.txt · Dernière modification: 2017/11/23 16:34 par aspicher
Sauf mention contraire, le contenu de ce wiki est placé sous la licence suivante:CC Attribution-
http://www.lacl.fr/~aspicher/Wiki/l2info/progc/xml 31/12/2017, 10?