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