Thu, 26 Dec 2024 19:26:37 +0100
add documentation for json value creation API
issue #532
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * \file json.h * \brief Interface for parsing data from JSON files. * \author Mike Becker * \author Olaf Wintermann * \copyright 2-Clause BSD License */ #ifndef UCX_JSON_H #define UCX_JSON_H #include "common.h" #include "allocator.h" #include "string.h" #include "buffer.h" #include "array_list.h" #include <string.h> #ifdef __cplusplus extern "C" { #endif /** * The type of the parsed token. */ enum cx_json_token_type { /** * No complete token parsed, yet. */ CX_JSON_NO_TOKEN, /** * The presumed token contains syntactical errors. */ CX_JSON_TOKEN_ERROR, /** * A "begin of array" '[' token. */ CX_JSON_TOKEN_BEGIN_ARRAY, /** * A "begin of object" '{' token. */ CX_JSON_TOKEN_BEGIN_OBJECT, /** * An "end of array" ']' token. */ CX_JSON_TOKEN_END_ARRAY, /** * An "end of object" '}' token. */ CX_JSON_TOKEN_END_OBJECT, /** * A colon ':' token separating names and values. */ CX_JSON_TOKEN_NAME_SEPARATOR, /** * A comma ',' token separating object entries or array elements. */ CX_JSON_TOKEN_VALUE_SEPARATOR, /** * A string token. */ CX_JSON_TOKEN_STRING, /** * A number token that can be represented as integer. */ CX_JSON_TOKEN_INTEGER, /** * A number token that cannot be represented as integer. */ CX_JSON_TOKEN_NUMBER, /** * A literal token. */ CX_JSON_TOKEN_LITERAL, /** * A white-space token. */ CX_JSON_TOKEN_SPACE }; /** * The type of some JSON value. */ enum cx_json_value_type { /** * Reserved. */ CX_JSON_NOTHING, // this allows us to always return non-NULL values /** * A JSON object. */ CX_JSON_OBJECT, /** * A JSON array. */ CX_JSON_ARRAY, /** * A string. */ CX_JSON_STRING, /** * A number that contains an integer. */ CX_JSON_INTEGER, /** * A number, not necessarily an integer. */ CX_JSON_NUMBER, /** * A literal (true, false, null). */ CX_JSON_LITERAL }; /** * JSON literal types. */ enum cx_json_literal { /** * The \c null literal. */ CX_JSON_NULL, /** * The \c true literal. */ CX_JSON_TRUE, /** * The \c false literal. */ CX_JSON_FALSE }; /** * Type alias for the token type enum. */ typedef enum cx_json_token_type CxJsonTokenType; /** * Type alias for the value type enum. */ typedef enum cx_json_value_type CxJsonValueType; /** * Type alias for the JSON parser interface. */ typedef struct cx_json_s CxJson; /** * Type alias for the token struct. */ typedef struct cx_json_token_s CxJsonToken; /** * Type alias for the JSON value struct. */ typedef struct cx_json_value_s CxJsonValue; /** * Type alias for the JSON array struct. */ typedef struct cx_json_array_s CxJsonArray; /** * Type alias for the JSON object struct. */ typedef struct cx_json_object_s CxJsonObject; /** * Type alias for a JSON string. */ typedef struct cx_mutstr_s CxJsonString; /** * Type alias for a number that can be represented as 64-bit signed integer. */ typedef int64_t CxJsonInteger; /** * Type alias for number that is not an integer. */ typedef double CxJsonNumber; /** * Type alias for a JSON literal. */ typedef enum cx_json_literal CxJsonLiteral; /** * Type alias for a key/value pair in a JSON object. */ typedef struct cx_json_obj_value_s CxJsonObjValue; /** * JSON array structure. */ struct cx_json_array_s { /** * The array data. */ CX_ARRAY_DECLARE(CxJsonValue*, array); }; /** * JSON object structure. */ struct cx_json_object_s { /** * The key/value entries. */ CX_ARRAY_DECLARE(CxJsonObjValue, values); }; /** * Structure for a key/value entry in a JSON object. */ struct cx_json_obj_value_s { /** * The key (or name in JSON terminology) of the value. */ cxmutstr name; /** * The value. */ CxJsonValue *value; }; /** * Structure for a JSON value. */ struct cx_json_value_s { /** * The allocator with which the value was allocated. * * Required for recursively deallocating memory of objects and arrays. */ const CxAllocator *allocator; /** * The type of this value. * * Specifies how the \c value union shall be resolved. */ CxJsonValueType type; /** * The value data. */ union { /** * The array data if type is #CX_JSON_ARRAY. */ CxJsonArray array; /** * The object data if type is #CX_JSON_OBJECT. */ CxJsonObject object; /** * The string data if type is #CX_JSON_STRING. */ CxJsonString string; /** * The integer if type is #CX_JSON_INTEGER. */ CxJsonInteger integer; /** * The number if type is #CX_JSON_NUMBER. */ CxJsonNumber number; /** * The literal type if type is #CX_JSON_LITERAL. */ CxJsonLiteral literal; } value; }; /** * Internally used structure for a parsed token. * * You should never need to use this in your code. */ struct cx_json_token_s { /** * The token type. */ CxJsonTokenType tokentype; /** * True, iff the \c content must be passed to cx_strfree(). */ bool allocated; /** * The token text, if any. * * This is not necessarily set when the token type already * uniquely identifies the content. */ cxmutstr content; }; /** * The JSON parser interface. */ struct cx_json_s { /** * The allocator used for produced JSON values. */ const CxAllocator *allocator; /** * The input buffer. */ CxBuffer buffer; /** * Used internally. * * Remembers the prefix of the last uncompleted token. */ CxJsonToken uncompleted; /** * A pointer to an intermediate state of the currently parsed value. * * Never access this value manually. */ CxJsonValue *parsed; /** * State stack. */ CX_ARRAY_DECLARE_SIZED(int, states, unsigned); /** * Value buffer stack. */ CX_ARRAY_DECLARE_SIZED(CxJsonValue*, vbuf, unsigned); /** * Internally reserved memory for the state stack. */ int states_internal[8]; /** * Internally reserved memory for the value buffer stack. */ CxJsonValue* vbuf_internal[8]; /** * Used internally. */ bool tokenizer_escape; // TODO: check if it can be replaced with look-behind }; /** * Status codes for the json interface. */ enum cx_json_status { /** * Everything is fine. */ CX_JSON_NO_ERROR, /** * The input buffer does not contain more data. */ CX_JSON_NO_DATA, /** * The input ends unexpectedly. * * Refill the buffer with cxJsonFill() to complete the json data. */ CX_JSON_INCOMPLETE_DATA, /** * Not used as a status and never returned by any function. * * You can use this enumerator to check for all "good" status results * by checking if the status is less than \c CX_JSON_OK. * * A "good" status means, that you can refill data and continue parsing. */ CX_JSON_OK, /** * Allocating memory for the internal buffer failed. */ CX_JSON_BUFFER_ALLOC_FAILED, /** * Allocating memory for a json value failed. */ CX_JSON_VALUE_ALLOC_FAILED, /** * A number value is incorrectly formatted. */ CX_JSON_FORMAT_ERROR_NUMBER, /** * The tokenizer found something unexpected. */ CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN }; /** * Typedef for the json status enum. */ typedef enum cx_json_status CxJsonStatus; /** * Initializes the json interface. * * @param json the json interface * @param allocator the allocator that shall be used for the produced values * @see cxJsonDestroy() */ cx_attr_nonnull_arg(1) void cxJsonInit(CxJson *json, const CxAllocator *allocator); /** * Destroys the json interface. * * @param json the json interface * @see cxJsonInit() */ cx_attr_nonnull void cxJsonDestroy(CxJson *json); /** * Destroys and re-initializes the json interface. * * You might want to use this, to reset the parser after * encountering a syntax error. * * @param json the json interface */ cx_attr_nonnull static inline void cxJsonReset(CxJson *json) { const CxAllocator *allocator = json->allocator; cxJsonDestroy(json); cxJsonInit(json, allocator); } /** * Adds more data to the input buffer. * * The data will be copied. * * @param json the json interface * @param buf the source buffer * @param len the length of the source buffer * @return zero on success, non-zero on internal allocation error * @see cxJsonFill() */ cx_attr_nonnull cx_attr_access_r(2, 3) int cxJsonFilln(CxJson *json, const char *buf, size_t len); #ifdef __cplusplus } // extern "C" cx_attr_nonnull static inline int cxJsonFill( CxJson *json, cxstring str ) { return cxJsonFilln(json, str.ptr, str.length); } cx_attr_nonnull static inline int cxJsonFill( CxJson *json, cxmutstr str ) { return cxJsonFilln(json, str.ptr, str.length); } cx_attr_nonnull cx_attr_cstr_arg(2) static inline int cxJsonFill( CxJson *json, const char *str ) { return cxJsonFilln(json, str, strlen(str)); } extern "C" { #else // __cplusplus /** * Adds more data to the input buffer. * * The data will be copied. * * @param json the json interface * @param str the string to add to the buffer * @return zero on success, non-zero on internal allocation error * @see cxJsonFilln() */ #define cxJsonFill(json, str) _Generic((str), \ cxstring: cx_json_fill_cxstr, \ cxmutstr: cx_json_fill_mutstr, \ char*: cx_json_fill_str, \ const char*: cx_json_fill_str) \ (json, str) /** * @copydoc cxJsonFill() */ cx_attr_nonnull static inline int cx_json_fill_cxstr( CxJson *json, cxstring str ) { return cxJsonFilln(json, str.ptr, str.length); } /** * @copydoc cxJsonFill() */ cx_attr_nonnull static inline int cx_json_fill_mutstr( CxJson *json, cxmutstr str ) { return cxJsonFilln(json, str.ptr, str.length); } /** * @copydoc cxJsonFill() */ cx_attr_nonnull cx_attr_cstr_arg(2) static inline int cx_json_fill_str( CxJson *json, const char *str ) { return cxJsonFilln(json, str, strlen(str)); } #endif /** * Creates a new (empty) JSON object. * * @param allocator the allocator to use * @return the new JSON object or \c NULL if allocation fails */ cx_attr_nodiscard CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); /** * Creates a new (empty) JSON array. * * @param allocator the allocator to use * @return the new JSON array or \c NULL if allocation fails */ cx_attr_nodiscard CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); /** * Creates a new JSON number value. * * @param allocator the allocator to use * @param num the numeric value * @return the new JSON value or \c NULL if allocation fails * @see cxJsonObjPutNumber() * @see cxJsonArrAddNumbers() */ cx_attr_nodiscard CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num); /** * Creates a new JSON number value based on an integer. * * @param allocator the allocator to use * @param num the numeric value * @return the new JSON value or \c NULL if allocation fails * @see cxJsonObjPutInteger() * @see cxJsonArrAddIntegers() */ cx_attr_nodiscard CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num); /** * Creates a new JSON string. * * @param allocator the allocator to use * @param str the string data * @return the new JSON value or \c NULL if allocation fails * @see cxJsonCreateCxString() * @see cxJsonObjPutString() * @see cxJsonArrAddStrings() */ cx_attr_nodiscard cx_attr_nonnull_arg(2) cx_attr_cstr_arg(2) CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str); /** * Creates a new JSON string. * * @param allocator the allocator to use * @param str the string data * @return the new JSON value or \c NULL if allocation fails * @see cxJsonCreateString() * @see cxJsonObjPutCxString() * @see cxJsonArrAddCxStrings() */ cx_attr_nodiscard CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str); /** * Creates a new JSON literal. * * @param allocator the allocator to use * @param lit the type of literal * @return the new JSON value or \c NULL if allocation fails * @see cxJsonObjPutLiteral() * @see cxJsonArrAddLiterals() */ cx_attr_nodiscard CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit); /** * Adds number values to a JSON array. * * @param arr the JSON array * @param num the array of values * @param count the number of elements * @return zero on success, non-zero on allocation failure */ cx_attr_nonnull cx_attr_access_r(2, 3) int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count); /** * Adds number values, of which all are integers, to a JSON array. * * @param arr the JSON array * @param num the array of values * @param count the number of elements * @return zero on success, non-zero on allocation failure */ cx_attr_nonnull cx_attr_access_r(2, 3) int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count); /** * Adds strings to a JSON array. * * The strings will be copied with the allocator of the array. * * @param arr the JSON array * @param str the array of strings * @param count the number of elements * @return zero on success, non-zero on allocation failure * @see cxJsonArrAddCxStrings() */ cx_attr_nonnull cx_attr_access_r(2, 3) int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count); /** * Adds strings to a JSON array. * * The strings will be copied with the allocator of the array. * * @param arr the JSON array * @param str the array of strings * @param count the number of elements * @return zero on success, non-zero on allocation failure * @see cxJsonArrAddStrings() */ cx_attr_nonnull cx_attr_access_r(2, 3) int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count); /** * Adds literals to a JSON array. * * @param arr the JSON array * @param lit the array of literal types * @param count the number of elements * @return zero on success, non-zero on allocation failure */ cx_attr_nonnull cx_attr_access_r(2, 3) int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count); /** * Add arbitrary values to a JSON array. * * \note In contrast to all other add functions, this function adds the values * directly to the array instead of copying them. * * @param arr the JSON array * @param val the values * @param count the number of elements * @return zero on success, non-zero on allocation failure */ cx_attr_nonnull cx_attr_access_r(2, 3) int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count); /** * Adds or replaces a value within a JSON object. * * The value will be directly added and not copied. * * \note If a value with the specified \p name already exists, * it will be (recursively) freed with its own allocator. * * @param obj the JSON object * @param name the name of the value * @param child the value * @return zero on success, non-zero on allocation failure */ cx_attr_nonnull int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child); /** * Creates a new JSON object and adds it to an existing object. * * @param obj the target JSON object * @param name the name of the new value * @return the new value or \c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateObj() */ cx_attr_nonnull CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name); /** * Creates a new JSON array and adds it to an object. * * @param obj the target JSON object * @param name the name of the new value * @return the new value or \c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateArr() */ cx_attr_nonnull CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name); /** * Creates a new JSON number and adds it to an object. * * @param obj the target JSON object * @param name the name of the new value * @param num the numeric value * @return the new value or \c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateNumber() */ cx_attr_nonnull CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num); /** * Creates a new JSON number, based on an integer, and adds it to an object. * * @param obj the target JSON object * @param name the name of the new value * @param num the numeric value * @return the new value or \c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateInteger() */ cx_attr_nonnull CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num); /** * Creates a new JSON string and adds it to an object. * * The string data is copied. * * @param obj the target JSON object * @param name the name of the new value * @param str the string data * @return the new value or \c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateString() */ cx_attr_nonnull cx_attr_cstr_arg(3) CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str); /** * Creates a new JSON string and adds it to an object. * * The string data is copied. * * @param obj the target JSON object * @param name the name of the new value * @param str the string data * @return the new value or \c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateCxString() */ cx_attr_nonnull CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str); /** * Creates a new JSON literal and adds it to an object. * * @param obj the target JSON object * @param name the name of the new value * @param lit the type of literal * @return the new value or \c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateLiteral() */ cx_attr_nonnull CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit); /** * Recursively deallocates the memory of a JSON value. * * \remark The type of each deallocated value will be changed * to #CX_JSON_NOTHING and values of such type will be skipped * by the de-allocation. That means, this function protects * you from double-frees when you are accidentally freeing * a nested value and then the parent value (or vice versa). * * @param value the value */ void cxJsonValueFree(CxJsonValue *value); /** * Tries to obtain the next JSON value. * * * @param json the json interface * @param value a pointer where the next value shall be stored * @return a status code */ cx_attr_nonnull cx_attr_access_w(2) CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value); /** * Checks if the specified value is a JSON object. * * @param value a pointer to the value * @return true if the value is a JSON object, false otherwise */ cx_attr_nonnull static inline bool cxJsonIsObject(const CxJsonValue *value) { return value->type == CX_JSON_OBJECT; } /** * Checks if the specified value is a JSON array. * * @param value a pointer to the value * @return true if the value is a JSON array, false otherwise */ cx_attr_nonnull static inline bool cxJsonIsArray(const CxJsonValue *value) { return value->type == CX_JSON_ARRAY; } /** * Checks if the specified value is a string. * * @param value a pointer to the value * @return true if the value is a string, false otherwise */ cx_attr_nonnull static inline bool cxJsonIsString(const CxJsonValue *value) { return value->type == CX_JSON_STRING; } /** * Checks if the specified value is a JSON number. * * This function will return true for both floating point and * integer numbers. * * @param value a pointer to the value * @return true if the value is a JSON number, false otherwise * @see cxJsonIsInteger() */ cx_attr_nonnull static inline bool cxJsonIsNumber(const CxJsonValue *value) { return value->type == CX_JSON_NUMBER || value->type == CX_JSON_INTEGER; } /** * Checks if the specified value is an integer number. * * @param value a pointer to the value * @return true if the value is an integer number, false otherwise * @see cxJsonIsNumber() */ cx_attr_nonnull static inline bool cxJsonIsInteger(const CxJsonValue *value) { return value->type == CX_JSON_INTEGER; } /** * Checks if the specified value is a JSON literal. * * JSON literals are \c true, \c false, and \c null. * * @param value a pointer to the value * @return true if the value is a JSON literal, false otherwise * @see cxJsonIsTrue() * @see cxJsonIsFalse() * @see cxJsonIsNull() */ cx_attr_nonnull static inline bool cxJsonIsLiteral(const CxJsonValue *value) { return value->type == CX_JSON_LITERAL; } /** * Checks if the specified value is a Boolean literal. * * @param value a pointer to the value * @return true if the value is either \c true or \c false, false otherwise * @see cxJsonIsTrue() * @see cxJsonIsFalse() */ cx_attr_nonnull static inline bool cxJsonIsBool(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL; } /** * Checks if the specified value is \c true. * * \remark Be advised, that this is not the same as * testing \c !cxJsonIsFalse(v). * * @param value a pointer to the value * @return true if the value is \c true, false otherwise * @see cxJsonIsBool() * @see cxJsonIsFalse() */ cx_attr_nonnull static inline bool cxJsonIsTrue(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE; } /** * Checks if the specified value is \c false. * * \remark Be advised, that this is not the same as * testing \c !cxJsonIsTrue(v). * * @param value a pointer to the value * @return true if the value is \c false, false otherwise * @see cxJsonIsBool() * @see cxJsonIsTrue() */ cx_attr_nonnull static inline bool cxJsonIsFalse(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE; } /** * Checks if the specified value is \c null. * * @param value a pointer to the value * @return true if the value is \c null, false otherwise * @see cxJsonIsLiteral() */ cx_attr_nonnull static inline bool cxJsonIsNull(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL; } /** * Obtains a C string from the given JSON value. * * If the \p value is not a string, the behavior is undefined. * * @param value the JSON value * @return the value represented as C string * @see cxJsonIsString() */ cx_attr_nonnull cx_attr_returns_nonnull static inline char *cxJsonAsString(const CxJsonValue *value) { return value->value.string.ptr; } /** * Obtains a UCX string from the given JSON value. * * If the \p value is not a string, the behavior is undefined. * * @param value the JSON value * @return the value represented as UCX string * @see cxJsonIsString() */ cx_attr_nonnull static inline cxstring cxJsonAsCxString(const CxJsonValue *value) { return cx_strcast(value->value.string); } /** * Obtains a mutable UCX string from the given JSON value. * * If the \p value is not a string, the behavior is undefined. * * @param value the JSON value * @return the value represented as mutable UCX string * @see cxJsonIsString() */ cx_attr_nonnull static inline cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { return value->value.string; } /** * Obtains a double-precision floating point value from the given JSON value. * * If the \p value is not a JSON number, the behavior is undefined. * * @param value the JSON value * @return the value represented as double * @see cxJsonIsNumber() */ cx_attr_nonnull static inline double cxJsonAsDouble(const CxJsonValue *value) { if (value->type == CX_JSON_INTEGER) { return (double) value->value.integer; } else { return value->value.number; } } /** * Obtains a 64-bit signed integer from the given JSON value. * * If the \p value is not a JSON number, the behavior is undefined. * If it is a JSON number, but not an integer, the value will be * converted to an integer, possibly losing precision. * * @param value the JSON value * @return the value represented as double * @see cxJsonIsNumber() * @see cxJsonIsInteger() */ cx_attr_nonnull static inline int64_t cxJsonAsInteger(const CxJsonValue *value) { if (value->type == CX_JSON_INTEGER) { return value->value.integer; } else { return (int64_t) value->value.number; } } /** * Obtains a Boolean value from the given JSON value. * * If the \p value is not a JSON literal, the behavior is undefined. * The \c null literal is interpreted as \c false. * * @param value the JSON value * @return the value represented as double * @see cxJsonIsLiteral() */ cx_attr_nonnull static inline bool cxJsonAsBool(const CxJsonValue *value) { return value->value.literal == CX_JSON_TRUE; } /** * Returns the size of a JSON array. * * If the \p value is not a JSON array, the behavior is undefined. * * @param value the JSON value * @return the size of the array * @see cxJsonIsArray() */ cx_attr_nonnull static inline size_t cxJsonArrSize(const CxJsonValue *value) { return value->value.array.array_size; } /** * Returns an element from a JSON array. * * If the \p value is not a JSON array, the behavior is undefined. * * This function guarantees to return a value. If the index is * out of bounds, the returned value will be of type * #CX_JSON_NOTHING, but never \c NULL. * * @param value the JSON value * @param index the index in the array * @return the value at the specified index * @see cxJsonIsArray() */ cx_attr_nonnull cx_attr_returns_nonnull CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); /** * Returns an iterator over the JSON array elements. * * The iterator yields values of type \c CxJsonValue* . * * If the \p value is not a JSON array, the behavior is undefined. * * @param value the JSON value * @return an iterator over the array elements * @see cxJsonIsArray() */ cx_attr_nonnull cx_attr_nodiscard CxIterator cxJsonArrIter(const CxJsonValue *value); /** * @copydoc cxJsonObjGet() */ cx_attr_nonnull cx_attr_returns_nonnull CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name); #ifdef __cplusplus } // extern "C" CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) { return cx_json_obj_get_cxstr(value, name); } CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) { return cx_json_obj_get_cxstr(value, cx_strcast(name)); } CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) { return cx_json_obj_get_cxstr(value, cx_str(name)); } extern "C" { #else /** * Returns a value corresponding to a key in a JSON object. * * If the \p value is not a JSON object, the behavior is undefined. * * This function guarantees to return a JSON value. If the * object does not contain \p name, the returned JSON value * will be of type #CX_JSON_NOTHING, but never \c NULL. * * @param value the JSON object * @param name the key to look up * @return the value corresponding to the key * @see cxJsonIsObject() */ #define cxJsonObjGet(value, name) _Generic((name), \ cxstring: cx_json_obj_get_cxstr, \ cxmutstr: cx_json_obj_get_mutstr, \ char*: cx_json_obj_get_str, \ const char*: cx_json_obj_get_str) \ (value, name) /** * @copydoc cxJsonObjGet() */ cx_attr_nonnull cx_attr_returns_nonnull static inline CxJsonValue *cx_json_obj_get_mutstr(const CxJsonValue *value, cxmutstr name) { return cx_json_obj_get_cxstr(value, cx_strcast(name)); } /** * @copydoc cxJsonObjGet() */ cx_attr_nonnull cx_attr_returns_nonnull cx_attr_cstr_arg(2) static inline CxJsonValue *cx_json_obj_get_str(const CxJsonValue *value, const char *name) { return cx_json_obj_get_cxstr(value, cx_str(name)); } #endif #ifdef __cplusplus } #endif #endif /* UCX_JSON_H */