Publi

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

xmlxmlHoy 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:

  • xmlReadFile() con la que leemos y procesamos el fichero XML
  • xmlDocGetRootElement() con la que obtenemos el nodo raíz del XML
  • xmlNodeGetContent() con la que obtenemos el contenido del nodo que le pasamos.
  • xmlGetProp con la que obtenemos el valor de una determinada propiedad del nodo.

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...

There are 7 comments left Ir a comentario

  1. Pingback: Bitacoras.com /

  2. SICOES /
    Usando Internet Explorer Internet Explorer 7.0 en Windows Windows XP

    muy buenos dias acabo de enterarme de tu blog y la verdad es que me parece genial no sabia de mas personas interesadas en estos temas, aqui tienes un nuevo lector que seguira visitandote abitualmente.

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 13.0.1 en Ubuntu Linux Ubuntu Linux

      Muchas gracias, perdona mi tardanza en contestar

  3. mohamed /
    Usando Mozilla Firefox Mozilla Firefox 34.0 en Windows Windows 7

    @Gaspar Fernández
    hola, me gustaria saber si me puedes ayudar en esto :
    “Realice un programa que lea el fichero books.xml (que podrá descargar de la
    página web de la asignatura), el cual contiene información sobre una lista de
    libros a comprar, busque en él los precios de los libros y los escriba en un
    fichero de texto precios.txt, uno por línea”
    y la verdad es que no tengo mucha idea como hacerlo :(, una ayuda por favor.

  4. Gaspar Fernández / Post Author
    Usando Mozilla Firefox Mozilla Firefox 35.0 en Ubuntu Linux Ubuntu Linux

    @mohamed
    Hola Mohamed, puedes empezar con el ejemplo de este post para ver las etiquetas que te vas encontrando en el XML, luego con un if seleccionar las etiquetas que te importan y escribir en el archivo la información de las mismas.

  5. Atilio /
    Usando Google Chrome Google Chrome 49.0.2623.87 en Windows Windows NT

    Muchas gracias por el aporte @Gaspar Fernández!! Me salieron algunos errores, podrías enviarme un email a xxxxxxxxx@xxxxxxx.com y contactarte conmigo para que te haga algunas preguntas puntuales y así me ayudas de paso? Sinceramente necesito ayuda!! Muchas gracias en serio!!

    Saludos

    Atilio

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 45.0 en Ubuntu Linux Ubuntu Linux

      Hola Atilio,
      E-mail enviado, aunque me gustaría que todas las dudas se compartieran con todos los lectores del blog. Seguramente se pueda hacer otro post ampliando el contenido 🙂

      Gracias por tu comentario

Leave a Reply