UAP Common Extensions 4.0 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 - it is not allocation-free.

Parser

The following listing shows the JSON parser API.

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

When all the data from the input buffer was successfully consumed, cxJsonNext() returns CX_JSON_NO_DATA.

If cxJsonNext() returns CX_JSON_INCOMPLETE_DATA, 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(). 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

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

The cxJsonAsXYZ() functions return the value with its corresponding plain 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().

Compare Values

#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

#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, size_t capacity); 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, size_t capacity); 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.

#include <cx/json.h> CxJsonValue* arr = cxJsonCreateArr(NULL, 2); // 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, "bool", CX_JSON_FALSE); cxJsonObjPutInteger(obj, "int", 47); CxJsonValue *strings = cxJsonObjPutArr(obj, "strings", 2); cxJsonArrAddStrings(strings, (const char*[]) {"hello", "world"}, 2); CxJsonValue *nested = cxJsonObjPutObj(obj, "nested"); CxJsonValue *objects = cxJsonObjPutArr(nested, "objects", 2); 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", 3), (double[]){3.1415, 47.11, 8.15}, 3); cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals", 3), (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); CxJsonValue *ints = cxJsonObjPutArr(nested, "ints", 3); cxJsonArrAddIntegers(ints, (int64_t[]){4, 8, 15}, 3); CxJsonValue *nested_array = cxJsonCreateArr(NULL, 2); 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, "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] } }

Clone Values

#include <cx/json.h> CxJsonValue* cxJsonClone(const CxJsonValue* value, const CxAllocator* allocator); CxJsonValue* cxJsonCloneFunc( CxJsonValue* target, const CxJsonValue* source, const CxAllocator* allocator, void *data);

The function cxJsonClone() creates a deep clone of the specified value using the specified allocator. The function cxJsonCloneFunc() is a cx_clone_func compatible version of the same function (the data argument is unused).

Writer

#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( const CxAllocator *allocator, CxJsonValue *value); cxmutstr cxJsonToPrettyString( const CxAllocator *allocator, CxJsonValue *value);

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

31 December 2025