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