UNIXworkcode

1 # JSON 2 3 The UCX JSON API allows [parsing](#parser) and [formatting](#writer) of JSON data. 4 5 The parser API is similar to the [properties](properties.h.md) parser, 6 but - due to the nature of JSON - is not allocation-free. 7 8 ## Parser 9 10 The following listing shows the JSON parser API. 11 12 > To simplify documentation, we introduce the pseudo-type `AnyStr` with the meaning that 13 > any UCX string and any C string are supported. 14 > The implementation is actually hidden behind a macro which uses `cx_strcast()` to guarantee compatibility. 15 {style="note"} 16 17 ```C 18 #include <cx/json.h> 19 20 void cxJsonInit(CxJson *json, const CxAllocator *allocator); 21 22 void cxJsonReset(CxJson *json); 23 24 int cxJsonFilln(CxJson *json, const char *buf, size_t len); 25 26 int cxJsonFill(CxJson *json, AnyStr str); 27 28 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value); 29 30 void cxJsonDestroy(CxJson *json); 31 32 CxJsonStatus cxJsonFromString(const CxAllocator *allocator, 33 AnyStr str, CxJsonValue **value); 34 ``` 35 36 The first step is to initialize a `CxJson` structure with a call to `cxJsonInit()`, 37 specifying the allocator that shall be used for allocating values of type `CxJsonValue`. 38 39 Specifying `NULL` as `allocator` is allowed, in which case the `cxDefaultAllocator` will be used. 40 41 The actual parsing is an interleaving invocation of the `cxJsonFill()` (or `cxJsonFilln()`) and `cxJsonNext()` functions. 42 The `cxJsonFill()` function is a convenience function that accepts UCX strings and normal zero-terminated C strings. 43 44 Calling `cxJsonNext()` will return with `CX_JSON_NO_ERROR` (= zero) for each JSON value that is successfully parsed, 45 and stores the pointer to the allocated value in the variable pointed to by `value`. 46 47 > The parser is capable of parsing multiple consecutive JSON values. 48 > If those values are not objects or arrays, they must, however, be separated by any whitespace character. 49 50 When all the data from the input buffer was successfully consumed, `cxJsonNext()` returns `CX_JSON_NO_DATA`. 51 52 If `cxJsonNext()` returns `CX_JSON_INCOMPLETE_DATA` it means that the input buffer is exhausted, 53 but the parsed input does not constitute a complete JSON value. 54 In that case, you can call `cxJsonFill()` again to add more data and continue with `cxJsonNext()`. 55 56 A complete list of all status codes can be seen [below](#list-of-status-codes). 57 58 If you want to reuse a `CxJson` structure, you can call `cxJsonReset()`. 59 In particular, you _must_ reset the parser when the last operation was a failure. 60 Otherwise, you need to call `cxJsonDestroy()` when you are done with the parser. 61 62 The function `cxJsonFromString()` combines the above procedure. 63 64 ### List of Status Codes 65 66 Below is a full list of status codes for `cxJsonNext()`. 67 68 | Status Code | Meaning | 69 |---------------------------------------|---------------------------------------------------------------------------------------------------| 70 | CX_JSON_NO_ERROR | A value was successfully parsed. | | 71 | CX_JSON_NO_DATA | The input buffer does not contain more data. | 72 | CX_JSON_INCOMPLETE_DATA | The input ends unexpectedly. Use `cxJsonFill()` to add more data before retrying. | 73 | CX_JSON_NULL_DATA | The input buffer was never initialized. Probably you forgot to call `cxJsonFill()` at least once. | 74 | CX_JSON_BUFFER_ALLOC_FAILED | More internal buffer was needed, but could not be allocated. | 75 | CX_JSON_VALUE_ALLOC_FAILED | Allocating memory for a json value failed. | 76 | CX_JSON_FORMAT_ERROR_NUMBER | A number value is incorrectly formatted. | 77 | CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN | The tokenizer found something unexpected, i.e. the JSON string contains a syntax error. | 78 79 ## Access Values 80 81 ```C 82 #include <cx/json.h> 83 84 bool cxJsonIsObject(const CxJsonValue *value); 85 86 bool cxJsonIsArray(const CxJsonValue *value); 87 88 bool cxJsonIsString(const CxJsonValue *value); 89 90 bool cxJsonIsNumber(const CxJsonValue *value); 91 92 bool cxJsonIsInteger(const CxJsonValue *value); 93 94 bool cxJsonIsLiteral(const CxJsonValue *value); 95 96 bool cxJsonIsBool(const CxJsonValue *value); 97 98 bool cxJsonIsTrue(const CxJsonValue *value); 99 100 bool cxJsonIsFalse(const CxJsonValue *value); 101 102 bool cxJsonIsNull(const CxJsonValue *value); 103 104 char *cxJsonAsString(const CxJsonValue *value); 105 106 cxstring cxJsonAsCxString(const CxJsonValue *value); 107 108 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value); 109 110 double cxJsonAsDouble(const CxJsonValue *value); 111 112 int64_t cxJsonAsInteger(const CxJsonValue *value); 113 114 bool cxJsonAsBool(const CxJsonValue *value); 115 116 size_t cxJsonArrSize(const CxJsonValue *value); 117 118 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); 119 120 CxJsonValue *cxJsonArrRemove(const CxJsonValue *value, size_t index); 121 122 size_t cxJsonObjSize(const CxJsonValue *value); 123 124 CxJsonValue *cxJsonObjGet(const CxJsonValue *value, AnyStr name); 125 126 CxJsonValue *cxJsonObjRemove(const CxJsonValue *value, AnyStr name); 127 128 CxIterator cxJsonArrIter(const CxJsonValue *value); 129 130 CxIterator cxJsonObjIter(const CxJsonValue *value); 131 ``` 132 133 The `cxJsonIsXYZ()` family functions check the type of the specified JSON value. 134 135 The JSON specification only defines numbers, therefore `cxJsonIsNumber()` returns true for both floating-point and integer numbers. 136 On the other hand, `cxJsonIsInteger()` only returns true for integral numbers. 137 138 The function `cxJsonIsBool()` returns true if `cxJsonIsLiteral()` returns true, but `cxJsonIsNull()` does not. 139 140 > Since a literal can be `true`, `false`, or `null`, note carefully that `!cxJsonIsTrue(v)` 141 > is in general _not_ equivalent to `cxJsonIsFalse(v)`. 142 > 143 > Additionally, UCX does implement the JavaScript concept of a "falsy" value, meaning that 144 > `cxJsonIsFalse()` _only_ returns true, if the value is a literal `false`. 145 >{style="note"} 146 147 The `cxJsonAsXYZ()` family of functions return the value with its corresponding C type. 148 149 The functions `cxJsonAsInteger()` and `cxJsonAsDouble()` can be used for any number value. 150 For example, if `cxJsonAsInteger()` is used on a non-integral number, a double-to-int conversion is performed. 151 152 The function `cxJsonArraySize()` returns the number of items in an array value, 153 which can be accessed via index with `cxJsonArrGet()` or via an iterator created with `cxJsonArrIter()`. 154 An element can be removed from an array with `cxJsonArrRemove()`. 155 156 The function `cxJsonObjGet()` returns the member in a JSON object associated with the specified `name`. 157 To remove a member, use `cxJsonObjRemove()`. 158 159 > Both `cxJsonArrGet()` and `cxJsonObjGet()` are safe regarding access to non-existing values. 160 > 161 > When `cxJsonArrGet()` is used with an out-of-bounds index, or `cxJsonObjGet()` is used with a non-existent name, 162 > they return a JSON value, that returns `false` for any `cxJsonIsXYZ()` function. 163 > 164 > This is **not** the case for `cxJsonArrRemove()` and `cxJsonObjRemove()`, which return `NULL` in that case. 165 166 > If you don't have full control over the JSON data, you should always check the datatype of a value first before accessing it. 167 >{style="note"} 168 169 ## Compare Values 170 171 ```C 172 #include <cx/json.h> 173 174 int cxJsonCompare(const CxJsonValue *value, 175 const CxJsonValue *other); 176 ``` 177 178 The function `cxJsonCompare()` performs a deep comparison of two JSON values. 179 It returns zero if both values are equal except for the order of object members. 180 When the values are not equal, the return-value is an unspecified non-zero value. 181 182 ## Deallocate Memory 183 184 ```C 185 #include <cx/json.h> 186 187 void cxJsonValueFree(CxJsonValue *value); 188 ``` 189 190 Once a JSON value is not needed anymore, the memory can be deallocated with `cxJsonValueFree()`. 191 Nested values are also recursively deallocated. 192 193 > Make sure that you are not accidentally deallocating values that are still part of an object or array. 194 > When deallocating the enclosing object/array, this will lead to a double-free. 195 >{style="warning"} 196 197 ## Create Objects 198 199 ```C 200 #include <cx/json.h> 201 202 CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); 203 204 CxJsonValue* cxJsonCreateArr( 205 const CxAllocator* allocator, size_t capacity); 206 207 CxJsonValue* cxJsonCreateNumber( 208 const CxAllocator* allocator, double num); 209 210 CxJsonValue* cxJsonCreateInteger( 211 const CxAllocator* allocator, int64_t num); 212 213 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, 214 AnyStr str); 215 216 CxJsonValue* cxJsonCreateLiteral( 217 const CxAllocator* allocator, CxJsonLiteral lit); 218 219 int cxJsonArrAddNumbers(CxJsonValue* arr, 220 const double* num, size_t count); 221 222 int cxJsonArrAddIntegers(CxJsonValue* arr, 223 const int64_t* num, size_t count); 224 225 int cxJsonArrAddStrings(CxJsonValue* arr, 226 const char* const* str, size_t count); 227 228 int cxJsonArrAddCxStrings(CxJsonValue* arr, 229 const cxstring* str, size_t count); 230 231 int cxJsonArrAddLiterals(CxJsonValue* arr, 232 const CxJsonLiteral* lit, size_t count); 233 234 int cxJsonArrAddValues(CxJsonValue* arr, 235 CxJsonValue* const* val, size_t count); 236 237 int cxJsonObjPut(CxJsonValue* obj, AnyStr name, CxJsonValue* child); 238 239 CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, AnyStr name); 240 241 CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, 242 AnyStr name, size_t capacity); 243 244 CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, 245 AnyStr name, double num); 246 247 CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, 248 AnyStr name, int64_t num); 249 250 CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, 251 AnyStr name, AnyStr str); 252 253 CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, 254 AnyStr name, CxJsonLiteral lit); 255 ``` 256 257 The `cxJsonCreateXY()`-family of functions can be used to create JSON values which are allocated by the specified `allocator`. 258 If you specify `NULL` as allocator, the `cxDefaultAllocator` is used. 259 260 If you want to add created values to a JSON array or JSON object, 261 you can use `cxJsonArrAddValues()` or `cxJsonObjPut()`, respectively. 262 However, it is usually more convenient to use one of the other functions, as they automatically create the JSON value for. 263 264 > The `capacity` argument for `cxJsonCreateArr()` is optional. 265 > When you specify zero, no memory for the data is allocated up-front, and the array 266 > is reallocated every time you add elements. 267 > If you can at least roughly estimate the number of elements the array will have, 268 > you can specify an initial capacity to save allocations. 269 270 ```C 271 #include <cx/json.h> 272 273 CxJsonValue* arr = cxJsonCreateArr(NULL, 2); 274 275 // this is equivalent... 276 CxJsonValue* x = cxJsonCreateInteger(NULL, 47); 277 CxJsonValue* y = cxJsonCreateInteger(NULL, 11); 278 cxJsonArrAddValues(arr, (CxJsonValue*[]){x, y}, 2); 279 280 // ... to this 281 cxJsonArrAddIntegers(arr, (int64_t[]){47, 11}, 2); 282 ``` 283 284 The following example shows how to construct a complete JSON object. 285 286 ```C 287 CxJsonValue *obj = cxJsonCreateObj(NULL); 288 289 cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE); 290 cxJsonObjPutInteger(obj, "int", 47); 291 292 CxJsonValue *strings = cxJsonObjPutArr(obj, "strings", 2); 293 cxJsonArrAddStrings(strings, (const char*[]) {"hello", "world"}, 2); 294 295 CxJsonValue *nested = cxJsonObjPutObj(obj, "nested"); 296 CxJsonValue *objects = cxJsonObjPutArr(nested, "objects", 2); 297 CxJsonValue *obj_in_arr[2] = { 298 cxJsonCreateObj(NULL), 299 cxJsonCreateObj(NULL), 300 }; 301 cxJsonObjPutInteger(obj_in_arr[0], "name1", 1); 302 cxJsonObjPutInteger(obj_in_arr[0], "name2", 3); 303 304 cxJsonObjPutInteger(obj_in_arr[1], "name2", 7); 305 cxJsonObjPutInteger(obj_in_arr[1], "name1", 3); 306 307 cxJsonArrAddValues(objects, obj_in_arr, 2); 308 309 cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats", 3), 310 (double[]){3.1415, 47.11, 8.15}, 3); 311 312 cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals", 3), 313 (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); 314 315 CxJsonValue *ints = cxJsonObjPutArr(nested, "ints", 3); 316 cxJsonArrAddIntegers(ints, (int64_t[]){4, 8, 15}, 3); 317 318 CxJsonValue *nested_array = cxJsonCreateArr(NULL, 2); 319 cxJsonArrAddValues(ints, &nested_array, 1); 320 cxJsonArrAddIntegers(nested_array, (int64_t[]){16, 23}, 2); 321 cxJsonArrAddIntegers(ints, (int64_t[]){42}, 1); 322 323 CxJsonWriter w = cxJsonWriterPretty(true); 324 cxJsonWrite(stdout, obj, (cx_write_func) fwrite, &w); 325 326 cxJsonValueFree(obj); 327 ``` 328 329 The above code writes the following output to `stdout`: 330 331 ```JSON 332 { 333 "bool": false, 334 "int": 47, 335 "strings": ["hello", "world"], 336 "nested": { 337 "objects": [{ 338 "name1": 1, 339 "name2": 3 340 }, { 341 "name2": 7, 342 "name1": 3 343 }], 344 "floats": [3.1415, 47.11, 8.15], 345 "literals": [true, null, false], 346 "ints": [4, 8, 15, [16, 23], 42] 347 } 348 } 349 ``` 350 351 ## Clone Values 352 353 ```C 354 #include <cx/json.h> 355 356 CxJsonValue* cxJsonClone(const CxJsonValue* value, 357 const CxAllocator* allocator); 358 359 CxJsonValue* cxJsonCloneFunc( 360 CxJsonValue* target, const CxJsonValue* source, 361 const CxAllocator* allocator, void *data); 362 ``` 363 364 The function `cxJsonClone()` creates a deep clone of the specified value using the specified allocator. 365 The function `cxJsonCloneFunc()` is a `cx_clone_func` compatible version of the same function 366 (the `data` argument is unused). 367 368 ## Writer 369 370 ```C 371 #include <cx/json.h> 372 373 typedef struct cx_json_writer_s { 374 bool pretty; 375 uint8_t frac_max_digits; 376 bool indent_space; 377 uint8_t indent; 378 bool escape_slash; 379 } CxJsonWriter; 380 381 CxJsonWriter cxJsonWriterCompact(void); 382 383 CxJsonWriter cxJsonWriterPretty(bool use_spaces); 384 385 int cxJsonWrite(void* target, const CxJsonValue* value, 386 cx_write_func wfunc, const CxJsonWriter* settings); 387 388 cxmutstr cxJsonToString( 389 const CxAllocator *allocator, CxJsonValue *value); 390 391 cxmutstr cxJsonToPrettyString( 392 const CxAllocator *allocator, CxJsonValue *value); 393 ``` 394 395 A JSON value can be formatted with the `cxJsonWrite()` function. 396 397 The `target` can be a stream, a UCX [buffer](buffer.h.md), or anything else that can be written to with a write function. 398 The behavior of the function is controlled via a `CxJsonWriter` struct. 399 With the functions `cxJsonWriterCompact()` and `cxJsonWriterPretty()` you can create default settings, 400 which you may modify to suit your needs (see the table below). 401 The functions `cxJsonToString()` and `cxJsonToPrettyString()` are convenience functions 402 which create a writer with default settings to produce a null-terminated string allocated by the specified allocator. 403 404 | Setting | Compact Default | Pretty Default | Description | 405 |-------------------|-----------------|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| 406 | `pretty` | `false` | `true` | If true, the JSON will be formatted with line breaks and tabs or spaces. If false, output is as compact as possible without extra characters. | 407 | `frac_max_digits` | 6 | 6 | The maximum number of fractional digits in a number value. | 408 | `indent_space` | ignored | depends on `use_spaces` argument | If true, use spaces for indentation, otherwise use tabs. | 409 | `indent` | ignored | 4 | If `indent_space` is `true`, this is the number of spaces per tab. Ignored otherwise. | 410 | `escape_slash` | `false` | `false` | If `true`, the slash character (a.k.a forward solidus: `/`) is also escaped. This is usually only needed when you want to use JSON as part of an HTML attribute. | 411 412 413 <seealso> 414 <category ref="apidoc"> 415 <a href="https://ucx.sourceforge.io/api/json_8h.html">json.h</a> 416 </category> 417 </seealso> 418