F4MP/f4mp_originalcode/thirdparty/zpl/code/source/json.c
Jous99 37b16f1547 code upload
codigo original de f4mp y tilted para referencias
2026-01-06 18:45:00 +01:00

802 lines
24 KiB
C

// file: source/json.c
////////////////////////////////////////////////////////////////
//
// JSON5 Parser
//
//
#ifdef ZPL_EDITOR
#include <zpl.h>
#endif
#include <math.h> /* needed for INFINITY and NAN */
ZPL_BEGIN_C_DECLS
static ZPL_ALWAYS_INLINE zpl_b32 zpl__json_is_special_char(char c) { return !!zpl_strchr("<>:/", c); }
static ZPL_ALWAYS_INLINE zpl_b32 zpl__json_is_assign_char(char c) { return !!zpl_strchr(":=|", c); }
static ZPL_ALWAYS_INLINE zpl_b32 zpl__json_is_delim_char(char c) { return !!zpl_strchr(",|\n", c); }
char *zpl__json_parse_object(zpl_json_object *obj, char *base, zpl_allocator a, zpl_u8 *err_code);
char *zpl__json_parse_array(zpl_json_object *obj, char *base, zpl_allocator a, zpl_u8 *err_code);
char *zpl__json_parse_value(zpl_json_object *obj, char *base, zpl_allocator a, zpl_u8 *err_code);
#define jx(x) !zpl_char_is_hex_digit(str[x])
ZPL_ALWAYS_INLINE zpl_b32 zpl__json_validate_name(char *str, char *err) {
while (*str) {
if ((str[0] == '\\' && !zpl_char_is_control(str[1])) &&
(str[0] == '\\' && jx(1) && jx(2) && jx(3) && jx(4))) {
*err = *str;
return false;
}
++str;
}
return true;
}
#undef jx
void zpl_json_parse(zpl_json_object *root, zpl_usize len, char const *source, zpl_allocator a, zpl_b32 handle_comments,
zpl_u8 *err_code) {
if (!root || !source)
{
ZPL_JSON_ASSERT;
if (err_code) *err_code = ZPL_JSON_ERROR_OBJECT_OR_SOURCE_WAS_NULL;
return;
}
zpl_unused(len);
char *dest = (char *)source;
if (handle_comments) {
zpl_b32 is_lit = false;
char lit_c = '\0';
char *p = dest;
char *b = dest;
zpl_isize l = 0;
while (*p) {
if (!is_lit) {
if ((*p == '"' || *p == '\'')) {
lit_c = *p;
is_lit = true;
++p;
continue;
}
} else {
if (*p == '\\' && *(p + 1) && *(p + 1) == lit_c) {
p += 2;
continue;
} else if (*p == lit_c) {
is_lit = false;
++p;
continue;
}
}
if (!is_lit) {
// NOTE(ZaKlaus): block comment
if (p[0] == '/' && p[1] == '*') {
b = p;
l = 2;
p += 2;
while (p[0] != '*' && p[1] != '/') {
++p;
++l;
}
p += 2;
l += 2;
zpl_memset(b, ' ', l);
}
// NOTE(ZaKlaus): inline comment
if (p[0] == '/' && p[1] == '/') {
b = p;
l = 2;
p += 2;
while (p[0] != '\n') {
++p;
++l;
}
++l;
zpl_memset(b, ' ', l);
}
}
++p;
}
}
if (err_code) *err_code = ZPL_JSON_ERROR_NONE;
zpl_json_object root_ = { 0 };
dest = zpl_str_trim(dest, false);
zpl_b32 starts_with_bracket = false;
if (*dest != '{' && *dest != '[') { root_.cfg_mode = true; }
if (*dest == '[') { starts_with_bracket = true; }
char *endp = NULL;
if (!starts_with_bracket)
endp = zpl__json_parse_object(&root_, dest, a, err_code);
else
endp = zpl__json_parse_array(&root_, (dest+1), a, err_code);
if (!root_.cfg_mode && endp == NULL) {
if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_VALUE;
}
// Replace root node with its child if the JSON document is an array.
if (starts_with_bracket && root_.type != ZPL_JSON_TYPE_ARRAY) {
zpl_json_object *replace = &root_;
*replace = root_.nodes[0];
}
*root = root_;
}
#define zpl___ind(x) \
for (int i = 0; i < x; ++i) zpl_fprintf(f, " ");
void zpl__json_write_value(zpl_file *f, zpl_json_object *o, zpl_json_object *t, zpl_isize indent, zpl_b32 is_inline, zpl_b32 is_last);
void zpl_json_write(zpl_file *f, zpl_json_object *o, zpl_isize indent) {
if (!o)
return;
zpl___ind(indent - 4);
if (!o->cfg_mode)
zpl_fprintf(f, "{\n");
else {
indent -= 4;
}
if (o->nodes)
{
zpl_isize cnt = zpl_array_count(o->nodes);
for (int i = 0; i < cnt; ++i) {
if (i < cnt - 1) {
zpl__json_write_value(f, o->nodes + i, o, indent, false, false);
} else {
zpl__json_write_value(f, o->nodes + i, o, indent, false, true);
}
}
}
zpl___ind(indent);
if (indent > 0) {
zpl_fprintf(f, "}");
} else {
if (!o->cfg_mode) zpl_fprintf(f, "}\n");
}
}
void zpl__json_write_value(zpl_file *f, zpl_json_object *o, zpl_json_object *t, zpl_isize indent, zpl_b32 is_inline, zpl_b32 is_last) {
zpl_json_object *node = o;
indent += 4;
if (!is_inline) {
zpl___ind(indent);
switch (node->name_style) {
case ZPL_JSON_NAME_STYLE_DOUBLE_QUOTE: {
zpl_fprintf(f, "\"%s\"", node->name);
} break;
case ZPL_JSON_NAME_STYLE_SINGLE_QUOTE: {
zpl_fprintf(f, "\'%s\'", node->name);
} break;
case ZPL_JSON_NAME_STYLE_NO_QUOTES: {
zpl_fprintf(f, "%s", node->name);
} break;
}
if (o->assign_style == ZPL_JSON_ASSIGN_STYLE_COLON)
zpl_fprintf(f, ": ");
else {
if (o->name_style != ZPL_JSON_NAME_STYLE_NO_QUOTES)
zpl___ind(1);
if (o->assign_style == ZPL_JSON_ASSIGN_STYLE_EQUALS)
zpl_fprintf(f, "= ");
else if (o->assign_style == ZPL_JSON_ASSIGN_STYLE_LINE)
zpl_fprintf(f, "| ");
}
}
switch (node->type) {
case ZPL_JSON_TYPE_STRING: {
zpl_fprintf(f, "\"%s\"", node->string);
} break;
case ZPL_JSON_TYPE_MULTISTRING: {
zpl_fprintf(f, "`%s`", node->string);
} break;
case ZPL_JSON_TYPE_ARRAY: {
zpl_fprintf(f, "[");
zpl_isize elemn = zpl_array_count(node->nodes);
for (int j = 0; j < elemn; ++j) {
if ((node->nodes + j)->type == ZPL_JSON_TYPE_OBJECT || (node->nodes + j)->type == ZPL_JSON_TYPE_ARRAY) {
zpl__json_write_value(f, node->nodes + j, o, 0, true, true);
} else {
zpl__json_write_value(f, node->nodes + j, o, -4, true, true);
}
if (j < elemn - 1) { zpl_fprintf(f, ", "); }
}
zpl_fprintf(f, "]");
} break;
case ZPL_JSON_TYPE_INTEGER: {
if (node->props == ZPL_JSON_PROPS_IS_HEX) {
zpl_fprintf(f, "0x%llx", (long long)node->integer);
} else {
zpl_fprintf(f, "%lld", (long long)node->integer);
}
} break;
case ZPL_JSON_TYPE_REAL: {
if (node->props == ZPL_JSON_PROPS_NAN) {
zpl_fprintf(f, "NaN");
} else if (node->props == ZPL_JSON_PROPS_NAN_NEG) {
zpl_fprintf(f, "-NaN");
} else if (node->props == ZPL_JSON_PROPS_INFINITY) {
zpl_fprintf(f, "Infinity");
} else if (node->props == ZPL_JSON_PROPS_INFINITY_NEG) {
zpl_fprintf(f, "-Infinity");
} else if (node->props == ZPL_JSON_PROPS_IS_EXP) {
zpl_fprintf(f, "%lld.%0*d%llde%c%lld", (long long)node->base, node->base2_offset, 0, (long long)node->base2, node->exp_neg ? '-' : '+',
(long long)node->exp);
} else if (node->props == ZPL_JSON_PROPS_IS_PARSED_REAL) {
if (!node->lead_digit)
zpl_fprintf(f, ".%0*d%lld", node->base2_offset, 0, (long long)node->base2);
else
zpl_fprintf(f, "%lld.%0*d%lld", (long long int)node->base2_offset, 0, (int)node->base, (long long)node->base2);
} else {
zpl_fprintf(f, "%f", node->real);
}
} break;
case ZPL_JSON_TYPE_OBJECT: {
zpl_json_write(f, node, indent);
} break;
case ZPL_JSON_TYPE_CONSTANT: {
if (node->constant == ZPL_JSON_CONST_TRUE) {
zpl_fprintf(f, "true");
} else if (node->constant == ZPL_JSON_CONST_FALSE) {
zpl_fprintf(f, "false");
} else if (node->constant == ZPL_JSON_CONST_NULL) {
zpl_fprintf(f, "null");
}
} break;
}
if (!is_inline) {
if (o->delim_style != ZPL_JSON_DELIM_STYLE_COMMA)
{
if (o->delim_style == ZPL_JSON_DELIM_STYLE_NEWLINE)
zpl_fprintf(f, "\n");
else if (o->delim_style == ZPL_JSON_DELIM_STYLE_LINE)
{
zpl___ind(o->delim_line_width);
zpl_fprintf(f, "|\n");
}
}
else
{
if (!is_last) {
zpl_fprintf(f, ",\n");
} else {
zpl_fprintf(f, "\n");
}
}
}
}
#undef zpl___ind
void zpl_json_free(zpl_json_object *obj) {
if ((obj->type == ZPL_JSON_TYPE_OBJECT || obj->type == ZPL_JSON_TYPE_ARRAY) && obj->nodes) {
for (zpl_isize i = 0; i < zpl_array_count(obj->nodes); ++i) { zpl_json_free(obj->nodes + i); }
zpl_array_free(obj->nodes);
}
}
char *zpl__json_parse_array(zpl_json_object *obj, char *base, zpl_allocator a, zpl_u8 *err_code) {
ZPL_ASSERT(obj && base);
char *p = base;
obj->type = ZPL_JSON_TYPE_ARRAY;
zpl_array_init(obj->nodes, a);
obj->backing = a;
while (*p) {
p = zpl_str_trim(p, false);
if (p && *p == ']') {
return p;
}
zpl_json_object elem = { 0 };
elem.backing = a;
p = zpl__json_parse_value(&elem, p, a, err_code);
if (err_code && *err_code != ZPL_JSON_ERROR_NONE) { return NULL; }
zpl_array_append(obj->nodes, elem);
p = zpl_str_trim(p, false);
if (*p == ',') {
++p;
continue;
} else {
if (*p != ']') {
if (err_code) { *err_code = ZPL_JSON_ERROR_INVALID_VALUE; }
}
return p;
}
}
if (err_code) { *err_code = ZPL_JSON_ERROR_INVALID_VALUE; }
return p;
}
char *zpl__json_parse_value(zpl_json_object *obj, char *base, zpl_allocator a, zpl_u8 *err_code) {
ZPL_ASSERT(obj && base);
char *p = base;
char *b = base;
char *e = base;
if (*p == '"' || *p == '\'') {
char c = *p;
obj->type = ZPL_JSON_TYPE_STRING;
b = p + 1;
e = b;
obj->string = b;
while (*e) {
if (*e == '\\' && *(e + 1) == c) {
e += 2;
continue;
} else if (*e == '\\' && (*(e + 1) == '\r' || *(e + 1) == '\n')) {
*e = ' ';
e++;
continue;
} else if (*e == c) {
break;
}
++e;
}
*e = '\0';
p = e + 1;
} else if (*p == '`') {
obj->type = ZPL_JSON_TYPE_MULTISTRING;
b = p + 1;
e = b;
obj->string = b;
while (*e) {
if (*e == '\\' && *(e + 1) == '`') {
e += 2;
continue;
} else if (*e == '`') {
break;
}
++e;
}
*e = '\0';
p = e + 1;
} else if (zpl_char_is_alpha(*p) || (*p == '-' && !zpl_char_is_digit(*(p + 1)))) {
obj->type = ZPL_JSON_TYPE_CONSTANT;
if (!zpl_strncmp(p, "true", 4)) {
obj->constant = ZPL_JSON_CONST_TRUE;
p += 4;
} else if (!zpl_strncmp(p, "false", 5)) {
obj->constant = ZPL_JSON_CONST_FALSE;
p += 5;
} else if (!zpl_strncmp(p, "null", 4)) {
obj->constant = ZPL_JSON_CONST_NULL;
p += 4;
} else if (!zpl_strncmp(p, "Infinity", 8)) {
obj->type = ZPL_JSON_TYPE_REAL;
obj->real = INFINITY;
obj->props = ZPL_JSON_PROPS_INFINITY;
p += 8;
} else if (!zpl_strncmp(p, "-Infinity", 9)) {
obj->type = ZPL_JSON_TYPE_REAL;
obj->real = -INFINITY;
obj->props = ZPL_JSON_PROPS_INFINITY_NEG;
p += 9;
} else if (!zpl_strncmp(p, "NaN", 3)) {
obj->type = ZPL_JSON_TYPE_REAL;
obj->real = NAN;
obj->props = ZPL_JSON_PROPS_NAN;
p += 3;
} else if (!zpl_strncmp(p, "-NaN", 4)) {
obj->type = ZPL_JSON_TYPE_REAL;
obj->real = -NAN;
obj->props = ZPL_JSON_PROPS_NAN_NEG;
p += 4;
} else {
ZPL_JSON_ASSERT;
if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_VALUE;
return NULL;
}
} else if (zpl_char_is_digit(*p) || *p == '+' || *p == '-' || *p == '.') {
obj->type = ZPL_JSON_TYPE_INTEGER;
b = p;
e = b;
zpl_isize ib = 0;
char buf[48] = { 0 };
if (*e == '+')
++e;
else if (*e == '-') {
buf[ib++] = *e++;
}
if (*e == '.') {
obj->type = ZPL_JSON_TYPE_REAL;
obj->props = ZPL_JSON_PROPS_IS_PARSED_REAL;
buf[ib++] = '0';
obj->lead_digit = false;
do {
buf[ib++] = *e;
} while (zpl_char_is_digit(*++e));
} else {
if (*e == '0' && (*(e + 1) == 'x' || *(e + 1) == 'X')) { obj->props = ZPL_JSON_PROPS_IS_HEX; }
while (zpl_char_is_hex_digit(*e) || *e == 'x' || *e == 'X') { buf[ib++] = *e++; }
if (*e == '.') {
obj->type = ZPL_JSON_TYPE_REAL;
obj->lead_digit = true;
zpl_u32 step = 0;
do {
buf[ib++] = *e;
++step;
} while (zpl_char_is_digit(*++e));
if (step < 2) { buf[ib++] = '0'; }
}
}
zpl_i32 exp = 0;
zpl_f32 eb = 10;
char expbuf[6] = { 0 };
zpl_isize expi = 0;
if (*e == 'e' || *e == 'E') {
++e;
if (*e == '+' || *e == '-' || zpl_char_is_digit(*e)) {
if (*e == '-') { eb = 0.1f; }
if (!zpl_char_is_digit(*e)) { ++e; }
while (zpl_char_is_digit(*e)) { expbuf[expi++] = *e++; }
}
exp = (zpl_i32)zpl_str_to_i64(expbuf, NULL, 10);
}
if (obj->type == ZPL_JSON_TYPE_INTEGER) {
obj->integer = zpl_str_to_i64(buf, 0, 0);
while (exp-- > 0) { obj->integer *= (zpl_i64)eb; }
} else {
obj->real = zpl_str_to_f64(buf, 0);
char *q = buf, *qp = q, *qp2 = q;
while (*qp != '.') ++qp;
*qp = '\0';
qp2 = qp + 1;
char *qpOff = qp2;
while (*qpOff++ == '0') obj->base2_offset++;
obj->base = (zpl_i32)zpl_str_to_i64(q, 0, 0);
obj->base2 = (zpl_i32)zpl_str_to_i64(qp2, 0, 0);
if (exp) {
obj->exp = exp;
obj->exp_neg = !(eb == 10.f);
obj->props = ZPL_JSON_PROPS_IS_EXP;
}
while (exp-- > 0) { obj->real *= eb; }
}
p = e;
} else if (*p == '[') {
p = zpl_str_trim(p + 1, false);
obj->type = ZPL_JSON_TYPE_ARRAY;
if (*p == ']') return (p+1);
p = zpl__json_parse_array(obj, p, a, err_code);
if (err_code && *err_code != ZPL_JSON_ERROR_NONE) { return NULL; }
++p;
} else if (*p == '{') {
p = zpl_str_trim(p + 1, false);
obj->type = ZPL_JSON_TYPE_OBJECT;
if (*p == '}') return (p+1);
p = zpl__json_parse_object(obj, p, a, err_code);
if (err_code && *err_code != ZPL_JSON_ERROR_NONE) { return NULL; }
++p;
}
return p;
}
char *zpl__json_parse_object(zpl_json_object *obj, char *base, zpl_allocator a, zpl_u8 *err_code) {
ZPL_ASSERT(obj && base);
char *p = base;
char *b = base;
char *e = base;
zpl_array_init(obj->nodes, a);
obj->backing = a;
p = zpl_str_trim(p, false);
zpl_b32 starts_with_brace = false;
zpl_b32 starts_with_bracket = false;
/**/ if (*p == '{') { ++p; starts_with_brace = true; obj->type = ZPL_JSON_TYPE_OBJECT; }
else if (*p == '[') { ++p; starts_with_bracket = true; obj->type = ZPL_JSON_TYPE_ARRAY; }
while (*p) {
zpl_json_object node = { 0 };
p = zpl_str_trim(p, false);
if (*p == '}' && starts_with_brace) return p;
if (*p == ']' && starts_with_bracket) return p;
if (*p == ']' && starts_with_brace) { if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_VALUE; return p; }
if (*p == '}' && starts_with_bracket) { if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_VALUE; return p; }
if (*p == '"' || *p == '\'') {
if (*p == '"') {
node.name_style = ZPL_JSON_NAME_STYLE_DOUBLE_QUOTE;
} else {
node.name_style = ZPL_JSON_NAME_STYLE_SINGLE_QUOTE;
}
char c = *p;
b = ++p;
e = zpl_str_control_skip(b, c);
node.name = b;
*e = '\0';
p = ++e;
p = zpl_str_trim(p, false);
if (!zpl__json_is_assign_char(*p)) {
ZPL_JSON_ASSERT;
if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_NAME;
return NULL;
}
}
else {
if (*p == '[') {
starts_with_bracket = true;
if (node.name) *node.name = '\0';
p = zpl__json_parse_value(&node, p, a, err_code);
goto l_parsed;
}
else if (zpl_char_is_alpha(*p) || *p == '_' || *p == '$') {
b = p;
e = b;
do {
++e;
} while (*e && (zpl_char_is_alphanumeric(*e) || *e == '_') && !zpl_char_is_space(*e) && !zpl__json_is_assign_char(*e));
if (zpl_char_is_space(*e)) {
// NOTE(zaklaus): We know this is where the node name ends, cut it here
*e = '\0';
++e;
}
if (zpl__json_is_assign_char(*e)) {
p = e;
if (*e == '=')
node.assign_style = ZPL_JSON_ASSIGN_STYLE_EQUALS;
else if (*e == '|')
node.assign_style = ZPL_JSON_ASSIGN_STYLE_LINE;
}
else {
while (*e) {
if (*e && (!zpl_char_is_space(*e) || zpl__json_is_assign_char(*e)))
{
if (*e == '=')
node.assign_style = ZPL_JSON_ASSIGN_STYLE_EQUALS;
else if (*e == '|')
node.assign_style = ZPL_JSON_ASSIGN_STYLE_LINE;
break;
}
++e;
}
e = zpl_str_trim(e, false);
p = e;
if (*p && !zpl__json_is_assign_char(*p)) {
ZPL_JSON_ASSERT;
if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_NAME;
return NULL;
}
else
{
if (*p == '=')
node.assign_style = ZPL_JSON_ASSIGN_STYLE_EQUALS;
else if (*p == '|')
node.assign_style = ZPL_JSON_ASSIGN_STYLE_LINE;
}
}
*e = '\0';
node.name = b;
node.name_style = ZPL_JSON_NAME_STYLE_NO_QUOTES;
}
}
char errc;
if (node.name && !zpl__json_validate_name(node.name, &errc)) {
ZPL_JSON_ASSERT;
if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_NAME;
return NULL;
}
p = zpl_str_trim(p + 1, false);
p = zpl__json_parse_value(&node, p, a, err_code);
node.backing = obj->backing;
if (err_code && *err_code != ZPL_JSON_ERROR_NONE) { return NULL; }
l_parsed:
if (err_code && *err_code != ZPL_JSON_ERROR_NONE) { return NULL; }
zpl_array_append(obj->nodes, node);
char *wp = p;
p = zpl_str_trim(p, true);
zpl_u8 wl = cast(zpl_u8)(p-wp);
if (zpl__json_is_delim_char(*p) || *p == '\0') {
zpl_json_object *n = zpl_array_end(obj->nodes);
if (*p == '\n')
n->delim_style = ZPL_JSON_DELIM_STYLE_NEWLINE;
else if (*p == '|') {
n->delim_style = ZPL_JSON_DELIM_STYLE_LINE;
n->delim_line_width = wl;
}
else if (*p == '\0') {
return p;
}
p = zpl_str_trim(p + 1, false);
if (*p == '\0' || *p == '}' || *p == ']')
return p;
else
continue;
} else if (*p == '\0' || *p == '}' || *p == ']') {
if (starts_with_brace && *p != '}')
{
if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_VALUE;
return NULL;
}
if (starts_with_bracket && *p != ']')
{
if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_VALUE;
return NULL;
}
return p;
} else {
ZPL_JSON_ASSERT;
if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_VALUE;
return NULL;
}
}
if (err_code) *err_code = ZPL_JSON_ERROR_INVALID_VALUE;
return p;
}
zpl_json_object *zpl_json_find(zpl_json_object *obj, char const *name, zpl_b32 deep_search)
{
if (obj->type != ZPL_JSON_TYPE_OBJECT)
{
return NULL;
}
for (zpl_isize i = 0; i < zpl_array_count(obj->nodes); i++)
{
if (!zpl_strcmp(obj->nodes[i].name, name))
{
return (obj->nodes + i);
}
}
if (deep_search)
{
for (zpl_isize i = 0; i < zpl_array_count(obj->nodes); i++)
{
zpl_json_object *res = zpl_json_find(obj->nodes + i, name, deep_search);
if (res != NULL)
return res;
}
}
return NULL;
}
void zpl_json_init_node(zpl_json_object *obj, zpl_allocator backing, char const *name, zpl_u8 type)
{
obj->name = (char *)name;
obj->type = type;
obj->backing = backing;
if (type == ZPL_JSON_TYPE_ARRAY || type == ZPL_JSON_TYPE_OBJECT)
{
zpl_array_init(obj->nodes, backing);
}
}
zpl_json_object *zpl_json_add_at(zpl_json_object *obj, zpl_isize index, char const *name, zpl_u8 type)
{
if (!obj || (obj->type != ZPL_JSON_TYPE_OBJECT && obj->type != ZPL_JSON_TYPE_ARRAY))
{
return NULL;
}
if (!obj->nodes)
return NULL;
if (index < 0 || index > zpl_array_count(obj->nodes))
return NULL;
zpl_json_object o = {0};
zpl_json_init_node(&o, obj->backing, name, type);
zpl_array_append_at(obj->nodes, o, index);
return obj->nodes + index;
}
zpl_json_object *zpl_json_add(zpl_json_object *obj, char const *name, zpl_u8 type)
{
if (!obj || (obj->type != ZPL_JSON_TYPE_OBJECT && obj->type != ZPL_JSON_TYPE_ARRAY))
{
return NULL;
}
if (!obj->nodes)
return NULL;
return zpl_json_add_at(obj, zpl_array_count(obj->nodes), name, type);
}
ZPL_END_C_DECLS