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 ## Deallocate Memory 170 171 ```C 172 #include <cx/json.h> 173 174 void cxJsonValueFree(CxJsonValue *value); 175 ``` 176 177 Once a JSON value is not needed anymore, the memory can be deallocated with `cxJsonValueFree()`. 178 Nested values are also recursively deallocated. 179 180 > Make sure that you are not accidentally deallocating values that are still part of an object or array. 181 > When deallocating the enclosing object/array, this will lead to a double-free. 182 >{style="warning"} 183 184 ## Create Objects 185 186 ```C 187 #include <cx/json.h> 188 189 CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); 190 191 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); 192 193 CxJsonValue* cxJsonCreateNumber( 194 const CxAllocator* allocator, double num); 195 196 CxJsonValue* cxJsonCreateInteger( 197 const CxAllocator* allocator, int64_t num); 198 199 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, 200 AnyStr str); 201 202 CxJsonValue* cxJsonCreateLiteral( 203 const CxAllocator* allocator, CxJsonLiteral lit); 204 205 int cxJsonArrAddNumbers(CxJsonValue* arr, 206 const double* num, size_t count); 207 208 int cxJsonArrAddIntegers(CxJsonValue* arr, 209 const int64_t* num, size_t count); 210 211 int cxJsonArrAddStrings(CxJsonValue* arr, 212 const char* const* str, size_t count); 213 214 int cxJsonArrAddCxStrings(CxJsonValue* arr, 215 const cxstring* str, size_t count); 216 217 int cxJsonArrAddLiterals(CxJsonValue* arr, 218 const CxJsonLiteral* lit, size_t count); 219 220 int cxJsonArrAddValues(CxJsonValue* arr, 221 CxJsonValue* const* val, size_t count); 222 223 int cxJsonObjPut(CxJsonValue* obj, AnyStr name, CxJsonValue* child); 224 225 CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, AnyStr name); 226 227 CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, AnyStr name); 228 229 CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, 230 AnyStr name, double num); 231 232 CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, 233 AnyStr name, int64_t num); 234 235 CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, 236 AnyStr name, AnyStr str); 237 238 CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, 239 AnyStr name, CxJsonLiteral lit); 240 ``` 241 242 The `cxJsonCreateXY()`-family of functions can be used to create JSON values which are allocated by the specified `allocator`. 243 If you specify `NULL` as allocator, the `cxDefaultAllocator` is used. 244 245 If you want to add created values to a JSON array or JSON object, 246 you can use `cxJsonArrAddValues()` or `cxJsonObjPut()`, respectively. 247 However, it is usually more convenient to use one of the other functions, as they automatically create the JSON value for. 248 249 ```C 250 #include <cx/json.h> 251 252 CxJsonValue* arr = cxJsonCreateArr(NULL); 253 254 // this is equivalent... 255 CxJsonValue* x = cxJsonCreateInteger(NULL, 47); 256 CxJsonValue* y = cxJsonCreateInteger(NULL, 11); 257 cxJsonArrAddValues(arr, (CxJsonValue*[]){x, y}, 2); 258 259 // ... to this 260 cxJsonArrAddIntegers(arr, (int64_t[]){47, 11}, 2); 261 ``` 262 263 The following example shows how to construct a complete JSON object. 264 265 ```C 266 CxJsonValue *obj = cxJsonCreateObj(NULL); 267 268 cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE); 269 cxJsonObjPutInteger(obj, "int", 47); 270 271 CxJsonValue *strings = cxJsonObjPutArr(obj, "strings"); 272 cxJsonArrAddStrings(strings, (const char*[]) {"hello", "world"}, 2); 273 274 CxJsonValue *nested = cxJsonObjPutObj(obj, "nested"); 275 CxJsonValue *objects = cxJsonObjPutArr(nested, "objects"); 276 CxJsonValue *obj_in_arr[2] = { 277 cxJsonCreateObj(NULL), 278 cxJsonCreateObj(NULL), 279 }; 280 cxJsonObjPutInteger(obj_in_arr[0], "name1", 1); 281 cxJsonObjPutInteger(obj_in_arr[0], "name2", 3); 282 283 cxJsonObjPutInteger(obj_in_arr[1], "name2", 7); 284 cxJsonObjPutInteger(obj_in_arr[1], "name1", 3); 285 286 cxJsonArrAddValues(objects, obj_in_arr, 2); 287 288 cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats"), 289 (double[]){3.1415, 47.11, 8.15}, 3); 290 291 cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals"), 292 (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); 293 294 CxJsonValue *ints = cxJsonObjPutArr(nested, "ints"); 295 cxJsonArrAddIntegers(ints, (int64_t[]){4, 8, 15}, 3); 296 297 CxJsonValue *nested_array = cxJsonCreateArr(NULL); 298 cxJsonArrAddValues(ints, &nested_array, 1); 299 cxJsonArrAddIntegers(nested_array, (int64_t[]){16, 23}, 2); 300 cxJsonArrAddIntegers(ints, (int64_t[]){42}, 1); 301 302 CxJsonWriter w = cxJsonWriterPretty(true); 303 cxJsonWrite(stdout, obj, (cx_write_func) fwrite, &w); 304 305 cxJsonValueFree(obj); 306 ``` 307 308 The above code writes the following output to `stdout`: 309 310 ```JSON 311 { 312 "bool": false, 313 "int": 47, 314 "strings": ["hello", "world"], 315 "nested": { 316 "objects": [{ 317 "name1": 1, 318 "name2": 3 319 }, { 320 "name2": 7, 321 "name1": 3 322 }], 323 "floats": [3.1415, 47.11, 8.15], 324 "literals": [true, null, false], 325 "ints": [4, 8, 15, [16, 23], 42] 326 } 327 } 328 ``` 329 330 ## Writer 331 332 ```C 333 #include <cx/json.h> 334 335 typedef struct cx_json_writer_s { 336 bool pretty; 337 uint8_t frac_max_digits; 338 bool indent_space; 339 uint8_t indent; 340 bool escape_slash; 341 } CxJsonWriter; 342 343 CxJsonWriter cxJsonWriterCompact(void); 344 345 CxJsonWriter cxJsonWriterPretty(bool use_spaces); 346 347 int cxJsonWrite(void* target, const CxJsonValue* value, 348 cx_write_func wfunc, const CxJsonWriter* settings); 349 ``` 350 351 A JSON value can be formatted with the `cxJsonWrite()` function. 352 353 The `target` can be a stream, a UCX [buffer](buffer.h.md), or anything else that can be written to with a write function. 354 The behavior of the function is controlled via a `CxJsonWriter` struct. 355 With the functions `cxJsonWriterCompact()` and `cxJsonWriterPretty()` you can create default settings, 356 which you may modify to suit your needs. 357 358 | Setting | Compact Default | Pretty Default | Description | 359 |-------------------|-----------------|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| 360 | `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. | 361 | `frac_max_digits` | 6 | 6 | The maximum number of fractional digits in a number value. | 362 | `indent_space` | ignored | depends on `use_spaces` argument | If true, use spaces for indentation, otherwise use tabs. | 363 | `indent` | ignored | 4 | If `indent_space` is `true`, this is the number of spaces per tab. Ignored otherwise. | 364 | `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. | 365 366 367 <seealso> 368 <category ref="apidoc"> 369 <a href="https://ucx.sourceforge.io/api/json_8h.html">json.h</a> 370 </category> 371 </seealso> 372