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