JSON
This page has been automatically translated using the Google Translate API services. We are working on improving texts. Thank you for your understanding and patience.
Functions
type* | json_read (...) |
type* | json_read_str (...) |
void | json_write (...) |
String* | json_write_str (...) |
void | json_destroy (...) |
void | json_destopt (...) |
JSON JavaScript Object Notation, is a data format in text mode that allows to easily represent basic types, objects and arrays. Although its use has become popular in the Web environment, it can also be used for other purposes, such as configuration files or local exchange. Its syntax is easy to understand for humans and simple to process for machines. In (Listing 1) we reproduce a small fragment of the JSON response of a 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..." }, ... ] } |
In its structure we can find these data types:
- Booleans: Represented by constants
true
orfalse
. - Numbers: Use the exponential notation of C for floating-point values:
2.3
,.76
,-0.54
or5.6e12
they are valid examples of numerical values. JSON does not distinguish between integers, negatives or reals. - Strings: Any text in quotes is considered a string. Supports any Unicode character in UTF-8 or through the escape sequence
\uXXXX to indicate the codepoint. - Arrays: Lists of items delimited by brackets
[...]
and separated by commas. The values do not have to be the same type as usually happens in some programming languages (Listing 2). - Objects: They are delimited by keys and composed of several fields separated by commas. Each field is formed by an identifier (string) followed by a colon and a value that can be any simple type, object or array (Listing 3).
- null: Indicates the absence of value.
- Binaries: JSON does not support binary data so opaque objects (images, for example) must be encoded in text and transmitted as a string type value. The most widespread and globally supported format is the Base64 where each character represents 6 bits of information.
1 2 3 |
[ "Red", "Green", "Blue", "Yellow" ] |
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 } } |
NAppGUI's JSON parser automatically transforms Image objects to Base64 and viceversa, allowing images to be embedded as data fields.
1. JSON parsing and conversion to data in C
NAppGUI allows automatic parsing of Json information.
- Use json_read to translate a Json to C.
- Use json_destroy to destroy a previously read object.
Next we will show different examples with basic types, arrays and objects. In Read/Write Json you have the complete code. The first step is to create a Stream with the content of the Json (Listing 4):
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)); |
The Stream should be destroyed with stm_close at the end of the analysis.
Later we will use json_read
indicating the expected data type of the Json.
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); |
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); |
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); |
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); |
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)); |
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)); |
For the analysis of objects it is necessary that we register with Data binding their structure, in such a way that the types and names of the fields of the Json object coincide with the struct
from C. Given this Json:
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 } ] } |
We define these structs
and register them:
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); |
This way we can now call json_read
:
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() ignores (skips) those fields of Json objects that are not registered with dbind. In no case will they generate caches or dynamic memory.
2. Mapping between Json and C
json_read recognizes the basic NAppGUI types, as well as String, Image, ArrSt, and ArrPt. Will not work with other data types such as int
or float
. It will also not recognize the STL structures vector
, map
, etc. In (Table 1) we show the equivalence between the fields of a Json and the C types that we need to map it correctly.
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. Convert from C to JSON
- Use json_write to write data/objects from C to Json.
Based again on (Table 1), let's do the reverse process and generate Json data from C types and objects. First, create a write stream to hold the result (Listing 14):
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); |
The Stream should be destroyed with stm_close when it is no longer needed.
Later we will use json_write
indicating the expected data type of the 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 |
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 |
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" |
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..." |
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 ] |
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" ] |
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 } ] } |
json_read ()
Parse a JSON script. It will transform JSON text into a type or object in C.
type* json_read(Stream *stm, const JsonOpts *opts, type);
stm | Data entry in JSON format. |
opts | Options. |
type | Type of data. |
Return
Result object.
Remarks
See JSON parsing and conversion to data in C.
json_read_str ()
Same as json_read, but accepting JSON code from a character string.
type* json_read_str(const char_t *str, const JsonOpts *opts, type);
str | Text string in JSON format, terminated with a null character |
opts | Options. |
type | Type of data. |
Return
Result object.
Remarks
See JSON parsing and conversion to data in C.
json_write ()
Write data in C to JSON format.
void json_write(Stream *stm, type *data, const JsonOpts *opts, type);
stm | Data output in JSON format. |
data | Object. |
opts | Options. |
type | Type of data. |
Remarks
json_write_str ()
Same as json_write, but returning the result in a String.
String* json_write_str(type *data, const JsonOpts *opts, type);
data | Object. |
opts | Options. |
type | Type of data. |
Return
Text string in JSON format.
Remarks
json_destroy ()
Destroys a JSON object, previously created with json_read .
void json_destroy(type **data, type);
data | Object. |
type | Type of data. |
json_destopt ()
Destroys a JSON object, previously created with json_read, if it is not NULL
.
void json_destopt(type **data, type);
data | Object. |
type | Type of data. |