Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

json_setf doesn's works as expected! #34

Open
Anricx opened this issue Jun 19, 2018 · 3 comments
Open

json_setf doesn's works as expected! #34

Anricx opened this issue Jun 19, 2018 · 3 comments

Comments

@Anricx
Copy link

Anricx commented Jun 19, 2018

char *payload = "{\"a\":1}";
struct json_out out = JSON_OUT_BUF(output, output_len);
json_setf(input, input_len, &out, ".sign", "%Q", "5738263BEFF11EC874D99623AC22A3B1");

output:

{"a":1,"sign":"5738263BEFF11EC874D99623AC22A3B1"}

Works grate

char *payload = "{\"service\":\"service.parking.detail\",\"a\":1}";
struct json_out out = JSON_OUT_BUF(output, output_len);
json_setf(input, input_len, &out, ".sign", "%Q", "5738263BEFF11EC874D99623AC22A3B1");

outpu:

{"service":"service.parking.detail","a":1,"59DEE02F6A5CBA08F66FA862BF88EF76"}

Not Working!

@cpq
Copy link
Member

cpq commented Aug 22, 2018

PR please :)

@zdila
Copy link

zdila commented Apr 21, 2020

In my case input JSON is {"ignore": "me"} (because of #26) and I am calling char *json = "{\"ignore\": \"me\"}"; json_setf(json, strlen(json), &out, ".foo", "{from: %ld, to: %ld, energy: %ld}", rec_start_ts, rec_finish_ts, rec_energy).

The result is: {"ignore": "me,"foo":{"from": 1587467683, "to": 1587558755, "energy": 59}"}

@rtrue983
Copy link

rtrue983 commented Jun 25, 2020

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;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants