UAP Common Extensions 3.1 Help

JSON

The UCX JSON API allows parsing and formatting of JSON data.

The parser API is similar to the properties parser, but - due to the nature of JSON - is not allocation-free.

Parser

#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);

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.

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.

If you want to reuse a CxJson structure, you can call cxJsonReset(), even if the last operation was a failure. Otherwise, you need to call cxJsonDestroy() when you are done with the parser.

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

#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 *cxJsonObjGet(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.

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().

The function cxJsonObjGet() returns the member in a JSON object associated with the specified name.

Deallocate Memory

#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.

Create Objects

#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, const char *str); CxJsonValue* cxJsonCreateCxString( const CxAllocator* allocator, cxstring 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, cxstring name, CxJsonValue* child); CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name); CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name); CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num); CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num); CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str); CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str); CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring 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.

#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.

CxJsonValue *obj = cxJsonCreateObj(NULL); cxJsonObjPutLiteral(obj, CX_STR("bool"), CX_JSON_FALSE); cxJsonObjPutInteger(obj, CX_STR("int"), 47); CxJsonValue *strings = cxJsonObjPutArr(obj, CX_STR("strings")); cxJsonArrAddStrings(strings, (const char*[]) {"hello", "world"}, 2); CxJsonValue *nested = cxJsonObjPutObj(obj, CX_STR("nested")); CxJsonValue *objects = cxJsonObjPutArr(nested, CX_STR("objects")); CxJsonValue *obj_in_arr[2] = { cxJsonCreateObj(NULL), cxJsonCreateObj(NULL), }; cxJsonObjPutInteger(obj_in_arr[0], CX_STR("name1"), 1); cxJsonObjPutInteger(obj_in_arr[0], CX_STR("name2"), 3); cxJsonObjPutInteger(obj_in_arr[1], CX_STR("name2"), 7); cxJsonObjPutInteger(obj_in_arr[1], CX_STR("name1"), 3); cxJsonArrAddValues(objects, obj_in_arr, 2); cxJsonArrAddNumbers(cxJsonObjPutArr(nested, CX_STR("floats")), (double[]){3.1415, 47.11, 8.15}, 3); cxJsonArrAddLiterals(cxJsonObjPutArr(nested, CX_STR("literals")), (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); CxJsonValue *ints = cxJsonObjPutArr(nested, CX_STR("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:

{ "bool": false, "int": 47, "nested": { "floats": [3.1415, 47.11, 8.15], "ints": [4, 8, 15, [16, 23], 42], "literals": [true, null, false], "objects": [{ "name1": 1, "name2": 3 }, { "name1": 3, "name2": 7 }] }, "strings": ["hello", "world"] }

Writer

#include <cx/json.h> typedef struct cx_json_writer_s { bool pretty; bool sort_members; 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);

A JSON value can be formatted with the cxJsonWrite() function.

The target can be a stream, a UCX buffer, 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.

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.

sort_members

true

true

If false members are written in the order in which they were added. If true, they are sorted lexicographically.

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.

Last modified: 06 April 2025