# JSON
The UCX JSON API allows [parsing](#parser) and [formatting](#writer) of JSON data.
The parser API is similar to the [properties](properties.h.md) parser,
but - due to the nature of JSON - is not allocation-free.
## Parser
The following listing shows the JSON parser API.
> To simplify documentation, we introduce the pseudo-type `AnyStr` with the meaning that
> any UCX string and any C string are supported.
> The implementation is actually hidden behind a macro which uses `cx_strcast()` to guarantee compatibility.
{style="note"}
```C
#include <cx/json.h>
void cxJsonInit(CxJson *json, const CxAllocator *allocator);
void cxJsonReset(CxJson *json);
int cxJsonFilln(CxJson *json, const char *buf, size_t len);
int cxJsonFill(CxJson *json, AnyStr str);
CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
void cxJsonDestroy(CxJson *json);
CxJsonStatus cxJsonFromString(const CxAllocator *allocator,
AnyStr str, CxJsonValue **value);
```
The first step is to initialize a `CxJson` structure with a call to `cxJsonInit()`,
specifying the allocator that shall be used for allocating values of type `CxJsonValue`.
Specifying `NULL` as `allocator` is allowed, in which case the `cxDefaultAllocator` will be used.
The actual parsing is an interleaving invocation of the `cxJsonFill()` (or `cxJsonFilln()`) and `cxJsonNext()` functions.
The `cxJsonFill()` function is a convenience function that accepts UCX strings and normal zero-terminated C strings.
Calling `cxJsonNext()` will return with `CX_JSON_NO_ERROR` (= zero) for each JSON value that is successfully parsed,
and stores the pointer to the allocated value in the variable pointed to by `value`.
> The parser is capable of parsing multiple consecutive JSON values.
> If those values are not objects or arrays, they must, however, be separated by any whitespace character.
When all the data from the input buffer was successfully consumed, `cxJsonNext()` returns `CX_JSON_NO_DATA`.
If `cxJsonNext()` returns `CX_JSON_INCOMPLETE_DATA` it means that the input buffer is exhausted,
but the parsed input does not constitute a complete JSON value.
In that case, you can call `cxJsonFill()` again to add more data and continue with `cxJsonNext()`.
A complete list of all status codes can be seen [below](#list-of-status-codes).
If you want to reuse a `CxJson` structure, you can call `cxJsonReset()`.
In particular, you _must_ reset the parser when the last operation was a failure.
Otherwise, you need to call `cxJsonDestroy()` when you are done with the parser.
The function `cxJsonFromString()` combines the above procedure.
### List of Status Codes
Below is a full list of status codes for `cxJsonNext()`.
| Status Code | Meaning |
|---------------------------------------|---------------------------------------------------------------------------------------------------|
| CX_JSON_NO_ERROR | A value was successfully parsed. | |
| CX_JSON_NO_DATA | The input buffer does not contain more data. |
| CX_JSON_INCOMPLETE_DATA | The input ends unexpectedly. Use `cxJsonFill()` to add more data before retrying. |
| CX_JSON_NULL_DATA | The input buffer was never initialized. Probably you forgot to call `cxJsonFill()` at least once. |
| CX_JSON_BUFFER_ALLOC_FAILED | More internal buffer was needed, but could not be allocated. |
| CX_JSON_VALUE_ALLOC_FAILED | Allocating memory for a json value failed. |
| CX_JSON_FORMAT_ERROR_NUMBER | A number value is incorrectly formatted. |
| CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN | The tokenizer found something unexpected, i.e. the JSON string contains a syntax error. |
## Access Values
```C
#include <cx/json.h>
bool cxJsonIsObject(const CxJsonValue *value);
bool cxJsonIsArray(const CxJsonValue *value);
bool cxJsonIsString(const CxJsonValue *value);
bool cxJsonIsNumber(const CxJsonValue *value);
bool cxJsonIsInteger(const CxJsonValue *value);
bool cxJsonIsLiteral(const CxJsonValue *value);
bool cxJsonIsBool(const CxJsonValue *value);
bool cxJsonIsTrue(const CxJsonValue *value);
bool cxJsonIsFalse(const CxJsonValue *value);
bool cxJsonIsNull(const CxJsonValue *value);
char *cxJsonAsString(const CxJsonValue *value);
cxstring cxJsonAsCxString(const CxJsonValue *value);
cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value);
double cxJsonAsDouble(const CxJsonValue *value);
int64_t cxJsonAsInteger(const CxJsonValue *value);
bool cxJsonAsBool(const CxJsonValue *value);
size_t cxJsonArrSize(const CxJsonValue *value);
CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
CxJsonValue *cxJsonArrRemove(const CxJsonValue *value, size_t index);
size_t cxJsonObjSize(const CxJsonValue *value);
CxJsonValue *cxJsonObjGet(const CxJsonValue *value, AnyStr name);
CxJsonValue *cxJsonObjRemove(const CxJsonValue *value, AnyStr name);
CxIterator cxJsonArrIter(const CxJsonValue *value);
CxIterator cxJsonObjIter(const CxJsonValue *value);
```
The `cxJsonIsXYZ()` family functions check the type of the specified JSON value.
The JSON specification only defines numbers, therefore `cxJsonIsNumber()` returns true for both floating-point and integer numbers.
On the other hand, `cxJsonIsInteger()` only returns true for integral numbers.
The function `cxJsonIsBool()` returns true if `cxJsonIsLiteral()` returns true, but `cxJsonIsNull()` does not.
> Since a literal can be `true`, `false`, or `null`, note carefully that `!cxJsonIsTrue(v)`
> is in general _not_ equivalent to `cxJsonIsFalse(v)`.
>
> Additionally, UCX does implement the JavaScript concept of a "falsy" value, meaning that
> `cxJsonIsFalse()` _only_ returns true, if the value is a literal `false`.
>{style="note"}
The `cxJsonAsXYZ()` family of functions return the value with its corresponding C type.
The functions `cxJsonAsInteger()` and `cxJsonAsDouble()` can be used for any number value.
For example, if `cxJsonAsInteger()` is used on a non-integral number, a double-to-int conversion is performed.
The function `cxJsonArraySize()` returns the number of items in an array value,
which can be accessed via index with `cxJsonArrGet()` or via an iterator created with `cxJsonArrIter()`.
An element can be removed from an array with `cxJsonArrRemove()`.
The function `cxJsonObjGet()` returns the member in a JSON object associated with the specified `name`.
To remove a member, use `cxJsonObjRemove()`.
> Both `cxJsonArrGet()` and `cxJsonObjGet()` are safe regarding access to non-existing values.
>
> When `cxJsonArrGet()` is used with an out-of-bounds index, or `cxJsonObjGet()` is used with a non-existent name,
> they return a JSON value, that returns `false` for any `cxJsonIsXYZ()` function.
>
> This is **not** the case for `cxJsonArrRemove()` and `cxJsonObjRemove()`, which return `NULL` in that case.
> If you don't have full control over the JSON data, you should always check the datatype of a value first before accessing it.
>{style="note"}
## Compare Values
```C
#include <cx/json.h>
int cxJsonCompare(const CxJsonValue *value,
const CxJsonValue *other);
```
The function `cxJsonCompare()` performs a deep comparison of two JSON values.
It returns zero if both values are equal except for the order of object members.
When the values are not equal, the return-value is an unspecified non-zero value.
## Deallocate Memory
```C
#include <cx/json.h>
void cxJsonValueFree(CxJsonValue *value);
```
Once a JSON value is not needed anymore, the memory can be deallocated with `cxJsonValueFree()`.
Nested values are also recursively deallocated.
> Make sure that you are not accidentally deallocating values that are still part of an object or array.
> When deallocating the enclosing object/array, this will lead to a double-free.
>{style="warning"}
## Create Objects
```C
#include <cx/json.h>
CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator);
CxJsonValue* cxJsonCreateNumber(
const CxAllocator* allocator, double num);
CxJsonValue* cxJsonCreateInteger(
const CxAllocator* allocator, int64_t num);
CxJsonValue* cxJsonCreateString(const CxAllocator* allocator,
AnyStr str);
CxJsonValue* cxJsonCreateLiteral(
const CxAllocator* allocator, CxJsonLiteral lit);
int cxJsonArrAddNumbers(CxJsonValue* arr,
const double* num, size_t count);
int cxJsonArrAddIntegers(CxJsonValue* arr,
const int64_t* num, size_t count);
int cxJsonArrAddStrings(CxJsonValue* arr,
const char* const* str, size_t count);
int cxJsonArrAddCxStrings(CxJsonValue* arr,
const cxstring* str, size_t count);
int cxJsonArrAddLiterals(CxJsonValue* arr,
const CxJsonLiteral* lit, size_t count);
int cxJsonArrAddValues(CxJsonValue* arr,
CxJsonValue* const* val, size_t count);
int cxJsonObjPut(CxJsonValue* obj, AnyStr name, CxJsonValue* child);
CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, AnyStr name);
CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, AnyStr name);
CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj,
AnyStr name, double num);
CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj,
AnyStr name, int64_t num);
CxJsonValue* cxJsonObjPutString(CxJsonValue* obj,
AnyStr name, AnyStr str);
CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj,
AnyStr name, CxJsonLiteral lit);
```
The `cxJsonCreateXY()`-family of functions can be used to create JSON values which are allocated by the specified `allocator`.
If you specify `NULL` as allocator, the `cxDefaultAllocator` is used.
If you want to add created values to a JSON array or JSON object,
you can use `cxJsonArrAddValues()` or `cxJsonObjPut()`, respectively.
However, it is usually more convenient to use one of the other functions, as they automatically create the JSON value for.
```C
#include <cx/json.h>
CxJsonValue* arr = cxJsonCreateArr(NULL);
// this is equivalent...
CxJsonValue* x = cxJsonCreateInteger(NULL, 47);
CxJsonValue* y = cxJsonCreateInteger(NULL, 11);
cxJsonArrAddValues(arr, (CxJsonValue*[]){x, y}, 2);
// ... to this
cxJsonArrAddIntegers(arr, (int64_t[]){47, 11}, 2);
```
The following example shows how to construct a complete JSON object.
```C
CxJsonValue *obj = cxJsonCreateObj(NULL);
cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE);
cxJsonObjPutInteger(obj, "int", 47);
CxJsonValue *strings = cxJsonObjPutArr(obj, "strings");
cxJsonArrAddStrings(strings, (const char*[]) {"hello", "world"}, 2);
CxJsonValue *nested = cxJsonObjPutObj(obj, "nested");
CxJsonValue *objects = cxJsonObjPutArr(nested, "objects");
CxJsonValue *obj_in_arr[2] = {
cxJsonCreateObj(NULL),
cxJsonCreateObj(NULL),
};
cxJsonObjPutInteger(obj_in_arr[0], "name1", 1);
cxJsonObjPutInteger(obj_in_arr[0], "name2", 3);
cxJsonObjPutInteger(obj_in_arr[1], "name2", 7);
cxJsonObjPutInteger(obj_in_arr[1], "name1", 3);
cxJsonArrAddValues(objects, obj_in_arr, 2);
cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats"),
(double[]){3.1415, 47.11, 8.15}, 3);
cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals"),
(CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3);
CxJsonValue *ints = cxJsonObjPutArr(nested, "ints");
cxJsonArrAddIntegers(ints, (int64_t[]){4, 8, 15}, 3);
CxJsonValue *nested_array = cxJsonCreateArr(NULL);
cxJsonArrAddValues(ints, &nested_array, 1);
cxJsonArrAddIntegers(nested_array, (int64_t[]){16, 23}, 2);
cxJsonArrAddIntegers(ints, (int64_t[]){42}, 1);
CxJsonWriter w = cxJsonWriterPretty(true);
cxJsonWrite(stdout, obj, (cx_write_func) fwrite, &w);
cxJsonValueFree(obj);
```
The above code writes the following output to `stdout`:
```JSON
{
"bool": false,
"int": 47,
"strings": ["hello", "world"],
"nested": {
"objects": [{
"name1": 1,
"name2": 3
}, {
"name2": 7,
"name1": 3
}],
"floats": [3.1415, 47.11, 8.15],
"literals": [true, null, false],
"ints": [4, 8, 15, [16, 23], 42]
}
}
```
## Writer
```C
#include <cx/json.h>
typedef struct cx_json_writer_s {
bool pretty;
uint8_t frac_max_digits;
bool indent_space;
uint8_t indent;
bool escape_slash;
} CxJsonWriter;
CxJsonWriter cxJsonWriterCompact(void);
CxJsonWriter cxJsonWriterPretty(bool use_spaces);
int cxJsonWrite(void* target, const CxJsonValue* value,
cx_write_func wfunc, const CxJsonWriter* settings);
cxmutstr cxJsonToString(CxJsonValue *value,
const CxAllocator *allocator);
cxmutstr cxJsonToPrettyString(CxJsonValue *value,
const CxAllocator *allocator);
```
A JSON value can be formatted with the `cxJsonWrite()` function.
The `target` can be a stream, a UCX [buffer](buffer.h.md), or anything else that can be written to with a write function.
The behavior of the function is controlled via a `CxJsonWriter` struct.
With the functions `cxJsonWriterCompact()` and `cxJsonWriterPretty()` you can create default settings,
which you may modify to suit your needs (see the table below).
The functions `cxJsonToString()` and `cxJsonToPrettyString()` are convenience functions
which create a writer with default settings to produce a null-terminated string allocated by the specified allocator.
| Setting | Compact Default | Pretty Default | Description |
|-------------------|-----------------|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `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. |
| `frac_max_digits` | 6 | 6 | The maximum number of fractional digits in a number value. |
| `indent_space` | ignored | depends on `use_spaces` argument | If true, use spaces for indentation, otherwise use tabs. |
| `indent` | ignored | 4 | If `indent_space` is `true`, this is the number of spaces per tab. Ignored otherwise. |
| `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. |
<seealso>
<category ref="apidoc">
<a href="https://ucx.sourceforge.io/api/json_8h.html">json.h</a>
</category>
</seealso>