json_setf does not work on empty json
Oelki opened this issue · 5 comments
Oelki commented
char json[50] = "{}";
char json_out[50];
struct json_out out = JSON_OUT_BUF(json_out, sizeof(json_out));
json_setf(json, sizeof(json), &out, ".somekey", "true");
only writes the value into json_out and thus creates an invalid json
printf("%s\n", json_out); // outut: "true"
cpq commented
PR please :)
marck-247 commented
Is this project still active? I am also stuck with the issue as described here...
cpq commented
Fix and send a PR.
zdila commented
I were just about to report this issue and found it here :-)
rtrue983 commented
I have a fix for this (at least with how I was using it) but don't have the time ATM to do a PR...
Here are the two functions that need to be modified:
static void json_vsetf_cb(void *userdata, const char *name, size_t name_len,
const char *path, const struct json_token *t) {
struct json_setf_data *data = (struct json_setf_data *) userdata;
int off, len = get_matched_prefix_len(path, data->json_path);
*int matched = 0;
if (t->ptr == NULL) return;
off = t->ptr - data->base;
// only check matched later on if deliminator or end are found
if (len > data->matched &&
(data->json_path[len] == '.' || data->json_path[len] == '\0')) {
matched = len;
}
/*
* If there is no exact path match, set the mutation position to be end
* of the object or array
*/
if (matched > data->matched && data->pos == 0 &&
(t->type == JSON_TYPE_OBJECT_END || t->type == JSON_TYPE_ARRAY_END))
{
// only save off matched value if it is a new and larger match
data->matched = matched;
// account for a the previous item being a string
if (data->base[data->prev] == '"') {
data->pos = data->end = data->prev + 1;
}
else {
data->pos = data->end = data->prev;
}
}
/* Exact path match. Set mutation position to the value of this token */
if (strcmp(path, data->json_path) == 0 && t->type != JSON_TYPE_OBJECT_START &&
t->type != JSON_TYPE_ARRAY_START) {
// need to set pos and end differently to account for '"' around string
if (t->type == JSON_TYPE_TRUE || t->type == JSON_TYPE_FALSE || t->type == JSON_TYPE_NUMBER) {
data->pos = off;
data->end = off + t->len + 1;
}
else {
data->pos = off - 1;
data->end = off + t->len + 2;
}
data->matched = matched;
}
/*
* For deletion, we need to know where the previous value ends, because
* we don't know where matched value key starts.
* When the mutation position is not yet set, remember each value end.
* When the mutation position is already set, but it is at the beginning
* of the object/array, we catch the end of the object/array and see
* whether the object/array start is closer then previously stored prev.
*/
if (data->pos == 0) {
data->prev = off + t->len; /* pos is not yet set */
} else if ((t->ptr[0] == '[' || t->ptr[0] == '{') && off + 1 < data->pos &&
off + 1 > data->prev) {
data->prev = off + 1;
}
(void) name;
(void) name_len;
}
int json_vsetf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, va_list ap) WEAK;
int json_vsetf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, va_list ap) {
struct json_setf_data data;
memset(&data, 0, sizeof(data));
data.json_path = json_path;
data.base = s;
data.end = len;
json_walk(s, len, json_vsetf_cb, &data);
if (json_fmt == NULL) {
/* Deletion codepath */
json_printf(out, "%.*s", data.prev, s);
/* Trim comma after the value that begins at object/array start */
if (s[data.prev - 1] == '{' || s[data.prev - 1] == '[') {
int i = data.end;
while (i < len && json_isspace(s[i])) i++;
if (s[i] == ',') data.end = i + 1; /* Point after comma */
}
json_printf(out, "%.*s", len - data.end, s + data.end);
} else {
/* Modification codepath */
int n, off = data.matched+1, depth = 0;
bool empty_base = false;
// if a partial or exact match isn't found, adjust a few items
if (data.matched == 0 && data.pos == 0) {
data.pos = data.prev - 1;
data.end--;
// a completely empty base requres additional correction
if (data.base[0] == '{' && data.base[1] == '}') {
empty_base = true;
data.pos = 1;
data.prev = 1;
data.end = 1;
}
}
/* Print the unchanged beginning */
json_printf(out, "%.*s", data.pos, s);
/* Add missing keys */
while ((n = strcspn(&json_path[off], ".[")) > 0) {
// don't add a comma if it is a completely empty base
if (s[data.prev - 1] != '{' && s[data.prev - 1] != '[' && depth == 0 && !empty_base) {
json_printf(out, ",");
}
if (off > 0 && json_path[off - 1] != '.') break;
json_printf(out, "%.*Q:", n, json_path + off);
off += n;
if (json_path[off] != '\0') {
json_printf(out, "%c", json_path[off] == '.' ? '{' : '[');
depth++;
off++;
}
}
/* Print the new value */
json_vprintf(out, json_fmt, ap);
/* Close brackets/braces of the added missing keys */
for (; off > data.matched; off--) {
int ch = json_path[off];
const char *p = ch == '.' ? "}" : ch == '[' ? "]" : "";
json_printf(out, "%s", p);
}
/* Print the rest of the unchanged string */
json_printf(out, "%.*s", len - data.end, s + data.end);
}
return data.end > data.pos ? 1 : 0;
}