/* * Copyright (c) 2012 Rogerz Zhang * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * * source here https://github.com/rogerz/jansson/blob/json_path/src/path.c */ #include #include #include #include "jansson_private.h" json_t *json_path_get(const json_t *json, const char *path) { static const char root_chr = '$', array_open = '['; static const char *path_delims = ".[", *array_close = "]"; const json_t *cursor; char *token, *buf, *peek, *endptr, delim = '\0'; const char *expect; if (!json || !path || path[0] != root_chr) return NULL; else buf = jsonp_strdup(path); peek = buf + 1; cursor = json; token = NULL; expect = path_delims; while (peek && *peek && cursor) { char *last_peek = peek; peek = strpbrk(peek, expect); if (peek) { if (!token && peek != last_peek) goto fail; delim = *peek; *peek++ = '\0'; } else if (expect != path_delims || !token) { goto fail; } if (expect == path_delims) { if (token) { cursor = json_object_get(cursor, token); } expect = (delim == array_open ? array_close : path_delims); token = peek; } else if (expect == array_close) { size_t index = strtol(token, &endptr, 0); if (*endptr) goto fail; cursor = json_array_get(cursor, index); token = NULL; expect = path_delims; } else { goto fail; } } jsonp_free(buf); return (json_t *)cursor; fail: jsonp_free(buf); return NULL; } int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error) { static const char root_chr = '$', array_open = '[', object_delim = '.'; static const char *const path_delims = ".[", *array_close = "]"; json_t *cursor, *parent = NULL; char *token, *buf = NULL, *peek, delim = '\0'; const char *expect; int index_saved = -1; jsonp_error_init(error, ""); if (!json || !path || flags || !value) { jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "invalid argument"); goto fail; } else { buf = jsonp_strdup(path); } if (buf[0] != root_chr) { jsonp_error_set(error, -1, -1, 0, json_error_invalid_format, "path should start with $"); goto fail; } peek = buf + 1; cursor = json; token = NULL; expect = path_delims; while (peek && *peek && cursor) { char *last_peek = peek; peek = strpbrk(last_peek, expect); if (peek) { if (!token && peek != last_peek) { jsonp_error_set(error, -1, -1, last_peek - buf, json_error_invalid_format, "unexpected trailing chars"); goto fail; } delim = *peek; *peek++ = '\0'; } else { // end of path if (expect == path_delims) { break; } else { jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "missing ']'?"); goto fail; } } if (expect == path_delims) { if (token) { if (token[0] == '\0') { jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "empty token"); goto fail; } parent = cursor; cursor = json_object_get(parent, token); if (!cursor) { if (!json_is_object(parent)) { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected"); goto fail; } if (delim == object_delim) { cursor = json_object(); json_object_set_new(parent, token, cursor); } else { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "new array is not allowed"); goto fail; } } } expect = (delim == array_open ? array_close : path_delims); token = peek; } else if (expect == array_close) { char *endptr; size_t index; parent = cursor; if (!json_is_array(parent)) { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array expected"); goto fail; } index = strtol(token, &endptr, 0); if (*endptr) { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid array index"); goto fail; } cursor = json_array_get(parent, index); if (!cursor) { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array index out of bound"); goto fail; } index_saved = index; token = NULL; expect = path_delims; } else { assert(1); jsonp_error_set(error, -1, -1, peek - buf, json_error_unknown, "unexpected error in path move"); goto fail; } } if (token) { if (json_is_object(cursor)) { json_object_set(cursor, token, value); } else { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected"); goto fail; } cursor = json_object_get(cursor, token); } else if (index_saved != -1 && json_is_array(parent)) { json_array_set(parent, index_saved, value); cursor = json_array_get(parent, index_saved); } else { jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid path"); goto fail; } json_decref(value); jsonp_free(buf); return 0; fail: json_decref(value); jsonp_free(buf); return -1; }