Poesía Binaria

Leer un archivo XML en C con libxml2 (con todos los nombres, atributos y contenidos)

Hoy en día, para muchas cosas se utiliza el metalenguaje XML ofrece una forma inteligible tanto para la máquina como para el humano.

Bien, el objetivo es que podamos leer y escribir información XML, y ahora toca hacerlo desde C, debemos ser capaces de extraer los nombres de las etiquetas, atributos y contenidos.

El código que muestro aquí es una vuelta de tuerca de uno de los ejemplos que aparecen en la web. En él, sólo se muestran los nombres de los nodos, yo he querido navegar dentro de cada nodo y mostrar contenidos y atributos. Con ayuda de este código podremos empezar a trabajar con archivos XML desde nuestros programas; aunque lo que muestro aquí sigue siendo un funcionamiento muy básico de libxml2.

Ejemplo de XML

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<Root>
        <Title>Poesía Binaria</Title>
        <URL type="site" owner="me">https://poesiabinaria.net</URL>
        <URL type="feed" owner="me" display="no">https://poesiabinaria.net/feed</URL>
        <Author>Gaspar Fernández</Author>
        <Categories>
                <Category id="linux" priority="1">Linux</Category>
                <Category id="ccpp" priority="3">C/C++</Category>
                <Category id="php" priority="2">PHP</Category>
        </Categories>
        <LastUpdate format="yyyy/mm/dd">2012/05/27</LastUpdate>
</Root>

El XML no vale para nada, es sólo un ejemplo con varios nodos con valores, atributos y demás, pero nos ayudará a comprender la salida final del programa.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <stdio.h>
#include <string.h>
#include <libxml/parser.h>
#include <ctype.h>

/* Lo podemos sacar de https://poesiabinaria.net/2010/03/trim-un-gran-amigo-php-c-c/ */
char *trim(char *s)
{
  char *start = s;

  /* Nos comemos los espacios al inicio */
  while(*start && isspace(*start))
    ++start;

  char *i = start;
  char *end = start;

  /* Nos comemos los espacios al final */
  while(*i)
  {
    if( !isspace(*(i++)) )
      end = i;
  }

  /* Escribimos el terminados */
  *end = 0;

  return start;
}

void print_nodes(xmlNode *first_child, int level)
{
  xmlNode *node;
  int l;
  xmlChar *value;
  xmlAttr *prop;

  for (node = first_child; node; node = node->next) {

    /* Si el nodo hijo es de texto y no tiene contenido... */
    if ( (node->type==3) && (strlen(trim((char*)xmlNodeGetContent(node)))==0) )
      continue;

    /* Escribimos tabs para dirigirnos al nivel deseado */
    for (l=0; l<level; l++)
      printf("\t");
   
    /* Averiguamos el valor del nodo */
    value=xmlNodeGetContent(node);
    printf("Child <%s> (t:%d) => "%s" ", node->name, node->type, trim((char*)value));

    /* Si tengo propiedades (atributos del nodo) las proceso */
    if (node->properties!=NULL)
      {
    printf (" props (");
    prop=node->properties;
    while (prop!=NULL)  /* Mientras haya una propiedad, la */
      {         /* voy a mostrar */
        value=xmlGetProp(node, prop->name);
        /* Si el valor no es NULL lo pongo en pantalla */
        if (value!=NULL)
          printf ("[%s]="%s" ", prop->name, xmlGetProp(node, prop->name));
        /* Me dirijo a la siguiente propiedad */
        prop=prop->next;
      }
    printf(")");
      }
    printf ("\n");
    /* Si el nodo tiene sub-nodos, los proceso recursivamente */
    if (node->children!=NULL)
      print_nodes(node->children, level+1);
  }

}

int main(int argc, char **argv) {
   xmlDoc *document;
   xmlNode *root, *first_child;
   char *filename;

   if (argc < 2) {
     fprintf(stderr, "Usage: %s filename.xml\n", argv[0]);
     return 1;
   }
   filename = argv[1];

  document = xmlReadFile(filename, NULL, 0);
  root = xmlDocGetRootElement(document);
  fprintf(stdout, "Root is <%s> (%i)\n", root->name, root->type);
  first_child = root->children;
  print_nodes(first_child, 1);
  fprintf(stdout, "Fin\n");
  return 0;
}

La función print_nodes nos dibujará una salida por pantalla de los nodos que encontramos, parecida a esta:

Root is <Root> (1)
Child <Title> (t:1) => «Poesía Binaria»
Child <text> (t:3) => «Poesía Binaria»
Child <URL> (t:1) => «https://poesiabinaria.net» props ([type]=»site» [owner]=»me» )
Child <text> (t:3) => «https://poesiabinaria.net»
Child <URL> (t:1) => «https://poesiabinaria.net/feed» props ([type]=»feed» [owner]=»me» [display]=»no» )
Child <text> (t:3) => «https://poesiabinaria.net/feed»
Child <Author> (t:1) => «Gaspar Fernández»
Child <text> (t:3) => «Gaspar Fernández»
Child <Categories> (t:1) => «Linux
C/C++
PHP»
Child <Category> (t:1) => «Linux» props ([id]=»linux» [priority]=»1″ )
Child <text> (t:3) => «Linux»
Child <Category> (t:1) => «C/C++» props ([id]=»ccpp» [priority]=»3″ )
Child <text> (t:3) => «C/C++»
Child <Category> (t:1) => «PHP» props ([id]=»php» [priority]=»2″ )
Child <text> (t:3) => «PHP»
Child <LastUpdate> (t:1) => «2012/05/27″ props ([format]=»yyyy/mm/dd» )
Child <text> (t:3) => «2012/05/27»
Fin

libxml2 nos almacenará todo el XML del fichero en una estructura donde podremos acceder a partir del nodo raíz como si de un árbol se tratase a cada una de las ramas de que disponemos.

Ya sean nodos, atributos de los nodos o sub-nodos, a través de xmlNode* node->(next o prev) podemos acceder a nuestros nodos hermanos en el mismo nivel que nosotros, por ejemplo dentro de <Categories&rt;, cada uno de los <Category…&rt; son hermanos.

También podemos acceder a los hijos con la propiedad children (primer hijo) o last (último hijo) de xmlNode. <Title&rt;, <URL&rt;, <Author&rt;, etc son hijos de <Root&rt;. Por otra parte, estando dentro de cualquier nodo, podemos acceder a la propiedad parent para dirigirnos al nodo padre que lo contiene.

Ahora repasaremos algunas de las funciones importantes como:

Para compilar el proyecto podemos utilizar esta línea:

$ gcc -o xml xml.c `xml2-config –cflags` -Wall `xml2-config –libs

Foto: Dan Zen (Flickr) Creative Commons a dia 28/05/2012

También podría interesarte....