SDK Multiplataforma en C logo

SDK Multiplataforma en C

JSON

❮ Anterior
Siguiente ❯

Funciones

type*json_read (...)
type*json_read_str (...)
voidjson_write (...)
String*json_write_str (...)
voidjson_destroy (...)
voidjson_destopt (...)

JSON JavaScript Object Notation, es un formato de datos en modo texto que permite representar de manera sencilla tipos básicos, objetos y arrays. Aunque su uso se ha popularizado en el ámbito Web puede utilizarse también para otros fines, como por ejemplo, archivos de configuración o intercambio local. Su sintaxis es fácil de entender para los humanos y sencilla de procesar para las máquinas. En (Listado 1) reproducimos un pequeño fragmento de la respuesta JSON de un servicio Web:

Listado 1: Fragmento JSON devuelto por un Web Service.
 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
{
    "code":0,
    "size":80,
    "data":[
    {
        "id":0,
        "code":"i7-8700K",
        "description":"Intel BX80684I78700K 8th Gen Core i7-8700K Processor",
        type":0,
        "price":374.89,
        "image":"cpu_00.jpg",
        "image64":"\/9j\/4AAQSkZJRgABAQ...."
    },
    {
        "id":1,
        "code":"G3930",
        "description":"Intel BX80677G3930 7th Gen Celeron Desktop Processors",
        "type":0,
        "price":51.95,
        "image":"cpu_01.jpg",
        "image64":"\/9j\/4AAQSkZJRgABAQAAAQABAAD..."
    },
    ...
    ]
}

En su estructura podemos encontrar estos tipos de datos:

  • Booleanos: Representados por las constantes true o false.
  • Números: Utiliza la notación exponencial de C para valores en coma flotante: 23, .76, -0.54 o 5.6e12 son ejemplos válidos de valores numéricos. JSON no distingue entre enteros, negativos o reales.
  • Cadenas: Cualquier texto entre comillas se considera una cadena. Admite cualquier carácter Unicode en UTF-8 o mediante la secuencia de escape \uXXXX para indicar el codepoint.
  • Arrays: Listas de valores delimitados por cochetes [ ... ] y separados por comas (Listado 2).
  • Listado 2: Array JSON
    1
    2
    3
    
    [
        "Red", "Green", "Blue", "Yellow"
    ]
    
  • Objetos: Están delimitados por llaves y compuestos por varios campos separados por comas. Cada campo lo forman un identificador (cadena) seguido del carácter dos puntos y un valor que puede ser cualquier tipo simple, objeto u array (Listado 3).
  • Listado 3: Objeto JSON
    1
    2
    3
    4
    5
    6
    7
    
    {
        "field1" : true,
        "field2" : 24.67,
        "field3" : "Hello Pibe",
        "field4" : [1, 2, 4, 8.4],
        "field5" : { "x" : 34.32, "y" : -6.19 }
    }
    
  • null: Indica la ausencia de valor.
  • Binarios: JSON no soporta datos binarios por lo que objetos opacos (imágenes, por ejemplo) deben ser codificados en texto y transmitidos como un valor de tipo cadena. El formato más extendido y soportado globalmente es el Base64 donde cada carácter representa 6 bits de información.
El parser JSON de NAppGUI transforma automáticamente los objetos Image a Base64 y viceversa, lo que permite incrustar imágenes como campos de datos.

1. Análisis de JSON y conversión a datos en C

NAppGUI permite el análisis automático de información Json.

  • Utiliza json_read para traducir un Json a C.
  • Utiliza json_destroy para destruir un objeto previamente leído.

Mostraremos a continuación diferentes ejemplos con tipos básicos, arrays y objetos. En Lectura/Escritura de Json tienes el código completo. El primer paso es crear un Stream con el contenido del Json (Listado 4):

Listado 4: Crear un Stream con datos Json.
1
2
3
4
5
6
7
8
9
/* Json data from web service */
Stream *stm = http_dget("http://serv.nappgui.com/dproducts.php", NULL, NULL);

/* Json data from disk file */
Stream *stm = hfile_stream("/home/fran/appdata/products.json", NULL);

/* Json data from memory block */
const char_t *data = "[12, 34, 67, 45]";
Stream *stm = stm_from_block((const byte_t*)data, str_len_c(data));
El Stream deberá destruirse con stm_close al finalizar el análisis.

Después utilizaremos json_read indicando el tipo de dato esperado del Json.

Listado 5: Json boolean.
1
2
3
4
5
json: true

bool_t *json = json_read(stm, NULL, bool_t);
bstd_printf("Json boolean: %d\n", *json);
json_destroy(&json, bool_t);
Listado 6: Json number.
1
2
3
4
5
json: 6654

uint16_t *json = json_read(stm, NULL, uint16_t);
bstd_printf("Json unsigned int: %d\n", *json);
json_destroy(&json, uint16_t);
Listado 7: Json string.
1
2
3
4
5
json: "Hello World"

String *json = json_read(stm, NULL, String);
bstd_printf("Json string: %s\n", tc(json));
json_destroy(&json, String);
Listado 8: Json string/b64 image (jpg, png, bmp).
1
2
3
4
5
6
7
json: "/9j/4QB4RXhpZgAASUkqAAgAAA..."

Image *json = json_read(stm, NULL, Image);
uint32_t width = image_width(json);
uint32_t height = image_height(json);
bstd_printf("Json image: width: %d height: %d\n", width, height);
json_destroy(&json, Image);
Listado 9: Json integer array
1
2
3
4
5
6
7
8
9
json: [ -321, 12, -8943, 228, -220, 347 ]

ArrSt(int16_t) *json = json_read(stm, NULL, ArrSt(int16_t));
bstd_printf("Json array: ");
arrst_foreach(id, json, int16_t)
    bstd_printf("%d ", *id);
arrst_end()
bstd_printf("\n");
json_destroy(&json, ArrSt(int16_t));
Listado 10: Json string array
1
2
3
4
5
6
7
8
9
json: [ "Red", "Green", "Blue", "Yellow", "Orange" ]

ArrPt(String) *json = json_read(stm, NULL, ArrPt(String));
bstd_printf("Json array: ");
arrpt_foreach(str, json, String)
    bstd_printf("%s ", tc(str));
arrpt_end()
bstd_printf("\n");
json_destroy(&json, ArrPt(String));

Para el análisis de objetos es necesario que registremos con Data binding la estructura de los mismos, de tal forma que coincidan los tipos y nombres de los campos del objeto Json con los struct de C. Dado este Json:

Listado 11: Json object
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
    "size" : 3,
    "data" : [
        {
            "description" : "Intel i7-7700K",
            "price" : 329.99
        },
        {
            "description" : "Ryzen-5-1600",
            "price" : 194.99
        },
        {
            "description" : "GTX-1060",
            "price" : 449.99
        }
    ]
}

Definimos estos struct y los registramos:

Listado 12: Estructuras que albergarán los datos del objeto Json.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _product_t Product;
typedef struct _products_t Products;

struct _product_t
{
    String *description;
    real32_t price;
};

struct _products_t
{
    uint32_t size;
    ArrSt(Product) *data;
};

DeclSt(Product);

dbind(Product, String*, description);
dbind(Product, real32_t, price);
dbind(Products, uint32_t, size);
dbind(Products, ArrSt(Product)*, data);

De esta forma ya podemos llamar a json_read:

Listado 13: Lectura del objeto Json.
1
2
3
4
5
6
7
Products *json = json_read(stm, NULL, Products);
bstd_printf("Json object: Size %d\n", json->size);
arrst_foreach(elem, json->data, Product)
    bstd_printf("Product: %s Price %.2f\n", tc(elem->description), elem->price);
arrst_end()
bstd_printf("\n");
json_destroy(&json, Products);
json_read() ignora (salta) aquellos campos de objetos Json que no estén registrados con dbind. En ningún caso generarán cachés ni memoria dinámica.

2. Mapeo entre Json y C

json_read reconoce los tipos básicos de NAppGUI, así como String, Image, ArrSt y ArrPt. No funcionará con otro tipo de datos como int o float. Tampoco reconocerá las estructuras STL vector, map, etc. En (Tabla 1) mostramos la equivalencia entre los campos de un Json y los tipos de C que necesitamos para mapearlo correctamente.

Tabla 1: Equivalencia entre tipos Json y NAppGUI.
Json C
boolean bool_t true, false
number int8_t, int16_t, int32_t, int64_t -6785, 45, 0
number uint8_t, uint16_t, uint32_t, uint64_t 1, 36734, 255, 0, 14
number real32_t, real64_t 67.554, -3.456, 1.5e7
string String "Intel Celeron", "Red"
string Image "/9j/4QB4RXhpZgAASUkqAAgAAA..."
array ArrSt(uint16_t) [ 12, 111, 865 ]
array ArrSt(real32_t) [ -34.89, 0.0001, 567.45, 1e6 ]
array ArrPt(String) [ "red", "green", "blue" ]
array ArrPt(Image) [ "/9j/4QB4RXh...", "/9j/4QB4RXh...", ... ]
object struct Product (Data binding) { "description" : "i7-8700K", " "price" : 234.54 }
array ArrSt(Product) [ { "description" : "i7-8700K", " "price" : 234.54 }, ... ]
array ArrPt(Product) [ { "description" : "i7-8700K", " "price" : 234.54 }, ... ]

3. Convertir desde C a JSON

  • Utiliza json_write para escribir datos/objetos desde C a Json.

Basándonos de nuevo en (Tabla 1), vamos a realizar el proceso inverso y generar datos Json a partir de tipos y objetos en C. Lo primero es crear un stream de escritura para albergar el resultado (Listado 14):

Listado 14: Crear un Stream de escritura.
1
2
3
4
5
/* Write stream in memory */
Stream *stm = stm_memory(2048);

/* Write stream in disk */
Stream *stm = stm_to_file("/home/fran/appdata/products.json", NULL);
El Stream deberá destruirse con stm_close cuando ya no sea necesario.

Después utilizaremos json_write indicando el tipo de dato esperado del Json.

Listado 15: Escribir booleano a Json.
1
2
3
4
5
bool_t data_bool = TRUE;
stm_writef(stm, "Json from bool_t: ");
json_write(stm, &data_bool, NULL, bool_t);

// Json from bool_t: true
Listado 16: Escribir entero a Json.
1
2
3
4
5
uint16_t data_uint = 6654;
stm_writef(stm, "Json from uint16_t: ");
json_write(stm, &data_uint, NULL, uint16_t);

// Json from uint16_t: 6654
Listado 17: Escribir String a Json.
1
2
3
4
5
6
String *data_str = str_c("Hello World");
stm_writef(stm, "Json from String: ");
json_write(stm, data_str, NULL, String);
str_destroy(&data_str);

// Json from String: "Hello World"
Listado 18: Escribir Image a Json.
1
2
3
4
5
6
Image *data_image = load_image();
stm_writef(stm, "Json from Image: ");
json_write(stm, data_image, NULL, Image);
image_destroy(&data_image);

// Json from Image: "iVBORw0KGgoAAAANSUhEUgAAAAIA..."
Listado 19: Escribir ArrSt(int16_t) a Json.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
ArrSt(int16_t) *array = arrst_create(int16_t);
arrst_append(array, -321, int16_t);
arrst_append(array, 12, int16_t);
arrst_append(array, -8943, int16_t);
arrst_append(array, 228, int16_t);
arrst_append(array, -220, int16_t);
arrst_append(array, 347, int16_t);
stm_writef(stm, "Json from int array: ");
json_write(stm, array, NULL, ArrSt(int16_t));
arrst_destroy(&array, NULL, int16_t);

// Json from int array: [ -321, 12, -8943, 228, -220, 347 ]
Listado 20: Escribir ArrPt(String) a Json.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ArrPt(String) *array = arrpt_create(String);
arrpt_append(array, str_c("Red"), String);
arrpt_append(array, str_c("Green"), String);
arrpt_append(array, str_c("Blue"), String);
arrpt_append(array, str_c("Yellow"), String);
arrpt_append(array, str_c("Orange"), String);
stm_writef(stm, "Json from string array: ");
json_write(stm, array, NULL, ArrPt(String));
arrpt_destroy(&array, str_destroy, String);

// Json from string array: [ "Red", "Green", "Blue", "Yellow", "Orange" ]
Listado 21: Escribir objeto Products a Json.
 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
Products *products = heap_new(Products);
products->size = 3;
products->data = arrst_create(Product);

{
    Product *product = arrst_new(products->data, Product);
    product->description = str_c("Intel i7-7700K");
    product->price = 329.99f;
}

{
    Product *product = arrst_new(products->data, Product);
    product->description = str_c("Ryzen-5-1600");
    product->price = 194.99f;
}

{
    Product *product = arrst_new(products->data, Product);
    product->description = str_c("GTX-1060");
    product->price = 449.99f;
}

stm_writef(stm, "Json from object: ");
json_write(stm, products, NULL, Products);
dbind_destroy(&products, Products);

// Json from object: {"size" : 3, "data" : [ {"description" : "Intel i7-7700K", "price" : 329.989990 }, {"description" : "Ryzen-5-1600", "price" : 194.990005 }, {"description" : "GTX-1060", "price" : 449.989990 } ] }
❮ Anterior
Siguiente ❯

json_read ()

Procesa un script JSON. Transformará texto JSON en un tipo u objeto en C.

type*
json_read(Stream *stm,
          const JsonOpts *opts,
          type);
stm

Entrada de datos en formato JSON.

opts

Opciones.

type

Tipo de datos.

Retorna

Objeto resultado.

Observaciones

Ver Análisis de JSON y conversión a datos en C.


json_read_str ()

Igual que json_read, pero aceptando el código JSON a partir de una cadena de caracteres.

type*
json_read_str(const char_t *str,
              const JsonOpts *opts,
              type);
str

Cadena de texto en formato JSON, acabada en carácter nulo '\0'.

opts

Opciones.

type

Tipo de datos.

Retorna

Objeto resultado.

Observaciones

Ver Análisis de JSON y conversión a datos en C.


json_write ()

Escribe datos en C a formato JSON.

void
json_write(Stream *stm,
           type *data,
           const JsonOpts *opts,
           type);
stm

Salida de datos en formato JSON.

data

Objeto.

opts

Opciones.

type

Tipo de datos.

Observaciones

Ver Convertir desde C a JSON.


json_write_str ()

Igual que json_write, pero devolviendo el resultado en un String.

String*
json_write_str(type *data,
               const JsonOpts *opts,
               type);
data

Objeto.

opts

Opciones.

type

Tipo de datos.

Retorna

Cadena de texto en formato JSON.

Observaciones

Ver Convertir desde C a JSON.


json_destroy ()

Destruye un objeto JSON, previamente creado con json_read.

void
json_destroy(type **data,
             type);
data

Objeto.

type

Tipo de datos.


json_destopt ()

Destruye un objeto JSON, previamente creado con json_read, si este no es NULL.

void
json_destopt(type **data,
             type);
data

Objeto.

type

Tipo de datos.

❮ Anterior
Siguiente ❯