En los títulos y los textos vais a encontrar unas cuantas citaciones cinematográficas (y si, soy un cinéfilo). Si no os interesan podéis fingir no verlas, ya que no son fundamentales para la comprensión de los post...

Este blog es la versión en Español de mi blog en Italiano L'arte della programmazione in C. Espero que mis traducciones sean comprensibles...

lunes, 7 de diciembre de 2015

Mission: Impossible - XML Deserializer
cómo escribir un XML Deserializer en C

Según lo prometido, volvemos con una nueva misión (no mucho) imposible para proponer una Deserializer complementario al Serializer del último post. Obviamente, si todavía no habéis leído el post anterior, deberíais:

1) avergonzaros
2) ir a leerlo. ¡Inmediatamente!


Ahora, si estáis leyendo esto, puede ser que ya habéis pasado los dos puntos anteriores, entonces podemos continuar. Vamos a utilizar también esta vez la libXML2 para escribir una función que consigue extraer todas las parejas key-value que componen un documento XML.
Siempre más fácil...
¡Vamos con el código!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <libxml/parser.h>

// prototipos locales
void deserialize(const char* document);
void recurDoc(xmlDocPtr doc, xmlNodePtr cur);

// main del programa de test
int main(int argc, char **argv)
{
    // test argumentos
    if (argc <= 1) {
        printf("Usage: %s docname\n", argv[0]);
        return(0);
    }

    /* libxml2: inicializa la librería y testea potenciales ABI mismatches
       entre la versión compilada y la actual shared library usada */
    LIBXML_TEST_VERSION;

    // deserializa XML
    deserialize(argv[1]);

    // sale con Ok
    return EXIT_SUCCESS;
}

// función deserialize()
void deserialize(
    const char* document)
{
    // elimina los blank nodes
    xmlKeepBlanksDefault(0);

    // extrae el documento del file
    xmlDocPtr doc;
    if ((doc = xmlParseFile(document)) == NULL ) {
        fprintf(stderr,"Document not parsed successfully. \n");
        return;
    }

    // test si el documento non está vacio
    xmlNodePtr cur;
    if ((cur = xmlDocGetRootElement(doc)) == NULL) {
        fprintf(stderr,"empty document\n");
        xmlFreeDoc(doc);
        return;
    }

    // recurre y libera el documento
    recurDoc(doc, cur);
    xmlFreeDoc(doc);
}

// función recursiva de lectura del documento
void recurDoc(
    xmlDocPtr  doc,
    xmlNodePtr cur)
{
    // loop de lectura
    xmlChar *value;
    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
        // extrae y libera un elemento
        if ((value = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) != NULL) {
            printf("key: %-10s - value: %s\n", cur->name, value);
            xmlFree(value);
        }

        // llamada recursiva
        recurDoc(doc, cur);

        // pasa al próximo elemento
        cur = cur->next;
    }

    // sale
    return;
}
Y, como siempre:

1) codigo que se auto-explica, ampliamente comentado y... los comentarios hablan por sí mismos.
2) la función main() no hace más que arrancar la función deserialize(): concéntrese en esa.


La función, en realidad, está dividida en dos partes: deserialize() prepara la lectura del documento y recurDoc() (que es una función recursiva que se llama a si misma) recurre el documento n-veces hasta que haya campos para leer.

Este ejemplo es apto para leer cualquier archivo XML que se le pasa como argumento, y se puede especializar fácilmente, por ejemplo si les pasais un file de estructura conocida (por ejemplo, se puede intentar con el catálogo XML de películas producidas por nuestro anterior Serializer) podeis rellenar los campos con lo campos key-value de la estructura de datos correspondiente (en nuestro caso la struct Catalog). En pocas palabras, con un poco de imaginación, la pareja Serializer/Deserializer que os he propuesto, permite una gran cantidad de actividades interesantes en programas que utilizan datos XML. Buen trabajo...

Os recuerdo una vez más que, para probar el programa en Linux (lo cual hice, por supuesto...) se debe instalar antes la libXML2 (desde el repository de la distribución), y luego compilar el programa con:
gcc desxml.c -I/usr/include/libxml2 -o desxml -lxml2
¡Hasta el próximo post!