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(const CxAllocator* allocator); 205 206 CxJsonValue* cxJsonCreateNumber( 207 const CxAllocator* allocator, double num); 208 209 CxJsonValue* cxJsonCreateInteger( 210 const CxAllocator* allocator, int64_t num); 211 212 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, 213 AnyStr str); 214 215 CxJsonValue* cxJsonCreateLiteral( 216 const CxAllocator* allocator, CxJsonLiteral lit); 217 218 int cxJsonArrAddNumbers(CxJsonValue* arr, 219 const double* num, size_t count); 220 221 int cxJsonArrAddIntegers(CxJsonValue* arr, 222 const int64_t* num, size_t count); 223 224 int cxJsonArrAddStrings(CxJsonValue* arr, 225 const char* const* str, size_t count); 226 227 int cxJsonArrAddCxStrings(CxJsonValue* arr, 228 const cxstring* str, size_t count); 229 230 int cxJsonArrAddLiterals(CxJsonValue* arr, 231 const CxJsonLiteral* lit, size_t count); 232 233 int cxJsonArrAddValues(CxJsonValue* arr, 234 CxJsonValue* const* val, size_t count); 235 236 int cxJsonObjPut(CxJsonValue* obj, AnyStr name, CxJsonValue* child); 237 238 CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, AnyStr name); 239 240 CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, AnyStr name); 241 242 CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, 243 AnyStr name, double num); 244 245 CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, 246 AnyStr name, int64_t num); 247 248 CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, 249 AnyStr name, AnyStr str); 250 251 CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, 252 AnyStr name, CxJsonLiteral lit); 253 ``` 254 255 The `cxJsonCreateXY()`-family of functions can be used to create JSON values which are allocated by the specified `allocator`. 256 If you specify `NULL` as allocator, the `cxDefaultAllocator` is used. 257 258 If you want to add created values to a JSON array or JSON object, 259 you can use `cxJsonArrAddValues()` or `cxJsonObjPut()`, respectively. 260 However, it is usually more convenient to use one of the other functions, as they automatically create the JSON value for. 261 262 ```C 263 #include <cx/json.h> 264 265 CxJsonValue* arr = cxJsonCreateArr(NULL); 266 267 // this is equivalent... 268 CxJsonValue* x = cxJsonCreateInteger(NULL, 47); 269 CxJsonValue* y = cxJsonCreateInteger(NULL, 11); 270 cxJsonArrAddValues(arr, (CxJsonValue*[]){x, y}, 2); 271 272 // ... to this 273 cxJsonArrAddIntegers(arr, (int64_t[]){47, 11}, 2); 274 ``` 275 276 The following example shows how to construct a complete JSON object. 277 278 ```C 279 CxJsonValue *obj = cxJsonCreateObj(NULL); 280 281 cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE); 282 cxJsonObjPutInteger(obj, "int", 47); 283 284 CxJsonValue *strings = cxJsonObjPutArr(obj, "strings"); 285 cxJsonArrAddStrings(strings, (const char*[]) {"hello", "world"}, 2); 286 287 CxJsonValue *nested = cxJsonObjPutObj(obj, "nested"); 288 CxJsonValue *objects = cxJsonObjPutArr(nested, "objects"); 289 CxJsonValue *obj_in_arr[2] = { 290 cxJsonCreateObj(NULL), 291 cxJsonCreateObj(NULL), 292 }; 293 cxJsonObjPutInteger(obj_in_arr[0], "name1", 1); 294 cxJsonObjPutInteger(obj_in_arr[0], "name2", 3); 295 296 cxJsonObjPutInteger(obj_in_arr[1], "name2", 7); 297 cxJsonObjPutInteger(obj_in_arr[1], "name1", 3); 298 299 cxJsonArrAddValues(objects, obj_in_arr, 2); 300 301 cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats"), 302 (double[]){3.1415, 47.11, 8.15}, 3); 303 304 cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals"), 305 (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); 306 307 CxJsonValue *ints = cxJsonObjPutArr(nested, "ints"); 308 cxJsonArrAddIntegers(ints, (int64_t[]){4, 8, 15}, 3); 309 310 CxJsonValue *nested_array = cxJsonCreateArr(NULL); 311 cxJsonArrAddValues(ints, &nested_array, 1); 312 cxJsonArrAddIntegers(nested_array, (int64_t[]){16, 23}, 2); 313 cxJsonArrAddIntegers(ints, (int64_t[]){42}, 1); 314 315 CxJsonWriter w = cxJsonWriterPretty(true); 316 cxJsonWrite(stdout, obj, (cx_write_func) fwrite, &w); 317 318 cxJsonValueFree(obj); 319 ``` 320 321 The above code writes the following output to `stdout`: 322 323 ```JSON 324 { 325 "bool": false, 326 "int": 47, 327 "strings": ["hello", "world"], 328 "nested": { 329 "objects": [{ 330 "name1": 1, 331 "name2": 3 332 }, { 333 "name2": 7, 334 "name1": 3 335 }], 336 "floats": [3.1415, 47.11, 8.15], 337 "literals": [true, null, false], 338 "ints": [4, 8, 15, [16, 23], 42] 339 } 340 } 341 ``` 342 343 ## Clone Values 344 345 ```C 346 #include <cx/json.h> 347 348 CxJsonValue* cxJsonClone(const CxJsonValue* value, 349 const CxAllocator* allocator); 350 351 CxJsonValue* cxJsonCloneFunc( 352 CxJsonValue* target, const CxJsonValue* source, 353 const CxAllocator* allocator, void *data); 354 ``` 355 356 The function `cxJsonClone()` creates a deep clone of the specified value using the specified allocator. 357 The function `cxJsonCloneFunc()` is a `cx_clone_func` compatible version of the same function 358 (the `data` argument is unused). 359 360 ## Writer 361 362 ```C 363 #include <cx/json.h> 364 365 typedef struct cx_json_writer_s { 366 bool pretty; 367 uint8_t frac_max_digits; 368 bool indent_space; 369 uint8_t indent; 370 bool escape_slash; 371 } CxJsonWriter; 372 373 CxJsonWriter cxJsonWriterCompact(void); 374 375 CxJsonWriter cxJsonWriterPretty(bool use_spaces); 376 377 int cxJsonWrite(void* target, const CxJsonValue* value, 378 cx_write_func wfunc, const CxJsonWriter* settings); 379 380 cxmutstr cxJsonToString(CxJsonValue *value, 381 const CxAllocator *allocator); 382 383 cxmutstr cxJsonToPrettyString(CxJsonValue *value, 384 const CxAllocator *allocator); 385 ``` 386 387 A JSON value can be formatted with the `cxJsonWrite()` function. 388 389 The `target` can be a stream, a UCX [buffer](buffer.h.md), or anything else that can be written to with a write function. 390 The behavior of the function is controlled via a `CxJsonWriter` struct. 391 With the functions `cxJsonWriterCompact()` and `cxJsonWriterPretty()` you can create default settings, 392 which you may modify to suit your needs (see the table below). 393 The functions `cxJsonToString()` and `cxJsonToPrettyString()` are convenience functions 394 which create a writer with default settings to produce a null-terminated string allocated by the specified allocator. 395 396 | Setting | Compact Default | Pretty Default | Description | 397 |-------------------|-----------------|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| 398 | `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. | 399 | `frac_max_digits` | 6 | 6 | The maximum number of fractional digits in a number value. | 400 | `indent_space` | ignored | depends on `use_spaces` argument | If true, use spaces for indentation, otherwise use tabs. | 401 | `indent` | ignored | 4 | If `indent_space` is `true`, this is the number of spaces per tab. Ignored otherwise. | 402 | `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. | 403 404 405 <seealso> 406 <category ref="apidoc"> 407 <a href="https://ucx.sourceforge.io/api/json_8h.html">json.h</a> 408 </category> 409 </seealso> 410