Implementación de cJSON para el manejo de JSON en C y C++

Introducción al formato JSON

JSON (JavaScript Object Notation) es un formato de intercambio de datos ligero y autodescriptivo. Su sintaxis deriva de JavaScript, pero es independiente del lenguaje. Los datos se organizan en pares clave-valor, separados por comas, donde los objetos se delimitan con llaves {} y los arreglos con corchetes []. Los valores pueden ser números, cadenas, booleanos, arreglos, objetos o nulos.

Descripción de la biblioteca cJSON

cJSON es un analizador JSON implementado en C puro (ANSI C89), compuesto por un único archivo de cabecera y uno de implementación. Utiliza una estructura de lista enlazada para representar los datos. Para integrarlo en un proyecto, basta con añadir los archivos cJSON.c y cJSON.h al código fuente.

Obtención y compilación

El código fuente se encuentra disponible en el repositorio de GitHub. Para clonarlo y compilarlo, se siguen estos pasos:

git clone https://github.com/DaveGamble/cJSON.git
mkdir compilacion
cd compilacion
cmake ..
make
sudo make install

La instalación predeterminada coloca los archivos de cabecera en /usr/local/include/cjson y las bibliotecas en /usr/local/lib. Se pueden personalizar parámetros durante la configuración con CMake, como deshabilitar pruebas o habilitar utilidades adicionales.

Estructuras de datos y funciones principales

La biblioteca define una estrucutra central para representar nodos JSON. Cada nodo incluye punteros para navegación en listas, campos para valores de diferentes tipos y un nombre para identificarlo en objetos.

#define TIPO_INVALIDO 0
#define TIPO_BOOLEANO_FALSO 1
#define TIPO_BOOLEANO_VERDADERO 2
#define TIPO_NULO 4
#define TIPO_NUMERO 8
#define TIPO_CADENA 16
#define TIPO_ARREGLO 32
#define TIPO_OBJETO 64

typedef struct NodoJSON {
    struct NodoJSON *anterior;
    struct NodoJSON *siguiente;
    struct NodoJSON *hijos;
    int tipo;
    char *cadena_valor;
    int entero_valor;
    double doble_valor;
    char *nombre_clave;
} NodoJSON;

Funciones de parseo y liberación

La función analizar_json() convierte una cadena JSON en una estructura de nodos. La función eliminar_json() libera la memoria asignada.

NodoJSON* analizar_json(const char* texto_json);
void eliminar_json(NodoJSON* nodo_raiz);

Funciones de acceso y manipulación

Para recuperar datos específicos, se usan funciones como obtener_elemento_arreglo() y buscar_en_objeto(). También existen funciones de creación para cada tipo de dato y métodos para agregar nodos a arreglos u objetos.

int cantidad_elementos_arreglo(NodoJSON* arreglo);
NodoJSON* obtener_elemento_arreglo(NodoJSON* arreglo, int indice);
NodoJSON* buscar_en_objeto(NodoJSON* objeto, const char* clave);
NodoJSON* crear_nodo_cadena(const char* texto);
void agregar_a_objeto(NodoJSON* objeto, const char* clave, NodoJSON* nuevo_nodo);

Ejemplos de uso práctico

Parseo simple de una cadena JSON

#include <stdio.h>
#include <stdlib.h>
#include "cjson/cJSON.h"

int main() {
    const char* datos_entrada = "{\"marca_temporal\":\"2023-10-05 14:30:00\", \"lectura\":42}";
    NodoJSON* raiz = analizar_json(datos_entrada);
    
    if (!raiz) {
        printf("Error en el JSON: %s\n", cJSON_GetErrorPtr());
        return EXIT_FAILURE;
    }
    
    NodoJSON* nodo_lectura = buscar_en_objeto(raiz, "lectura");
    if (nodo_lectura && nodo_lectura->tipo == TIPO_NUMERO) {
        printf("Valor numérico: %d\n", nodo_lectura->entero_valor);
    }
    
    NodoJSON* nodo_tiempo = buscar_en_objeto(raiz, "marca_temporal");
    if (nodo_tiempo && nodo_tiempo->tipo == TIPO_CADENA) {
        printf("Marca temporal: %s\n", nodo_tiempo->cadena_valor);
    }
    
    eliminar_json(raiz);
    return EXIT_SUCCESS;
}

Construcción programática de un objeto JSON

#include <stdio.h>
#include <stdlib.h>
#include "cjson/cJSON.h"

int main() {
    NodoJSON* raiz = crear_nodo_objeto();
    NodoJSON* seccion = crear_nodo_objeto();
    
    agregar_a_objeto(raiz, "resultado", crear_nodo_entero(0));
    agregar_a_objeto(raiz, "accion", crear_nodo_cadena("REPRODUCIR"));
    agregar_a_objeto(raiz, "titulo", crear_nodo_cadena("Canción favorita"));
    agregar_a_objeto(raiz, "detalle", seccion);
    agregar_a_objeto(seccion, "artista", crear_nodo_cadena("Artista Desconocido"));
    agregar_a_objeto(seccion, "duracion", crear_nodo_entero(180));
    
    char* texto_salida = cJSON_Print(raiz);
    printf("JSON generado:\n%s\n", texto_salida);
    free(texto_salida);
    
    eliminar_json(raiz);
    return EXIT_SUCCESS;
}

Recorrido recursivo de un JSON complejo

#include <stdio.h>
#include <stdlib.h>
#include "cjson/cJSON.h"

void mostrar_valores_internos(NodoJSON* nodo_actual) {
    for (int i = 0; i < cantidad_elementos_arreglo(nodo_actual); i++) {
        NodoJSON* elemento = obtener_elemento_arreglo(nodo_actual, i);
        if (elemento->tipo == TIPO_OBJETO) {
            mostrar_valores_internos(elemento);
        } else {
            printf("Clave: %s, Valor: %s\n", elemento->nombre_clave, cJSON_Print(elemento));
        }
    }
}

int main() {
    const char* json_complejo = "{\"config\":{\"servidor\":\"localhost\"}, \"activo\":true, \"lista\":[1,2,3]}";
    NodoJSON* raiz = analizar_json(json_complejo);
    
    if (raiz) {
        printf("Contenido completo:\n%s\n\n", cJSON_Print(raiz));
        printf("Valores en el nivel más profundo:\n");
        mostrar_valores_internos(raiz);
        eliminar_json(raiz);
    }
    return EXIT_SUCCESS;
}

Etiquetas: cJSON C C++ json parseo

Publicado el 6-1 11:10