summaryrefslogtreecommitdiff
path: root/jsonpath.c
diff options
context:
space:
mode:
authorJoey Adams2010-07-14 04:05:24 +0000
committerJoey Adams2010-07-14 04:05:24 +0000
commit46d4111846c2fdd0a2790c25ba51c8998d8ab218 (patch)
tree551e0be5d2569a248cf2ca95864d9bbd33fe5c74 /jsonpath.c
parent442fe529e29fc5538dc4172246758f68a4156106 (diff)
Refactored jsonpath a bit by introducing JPRef.
JPRef is a structure representing an item matched by jp_match. Before, plain old json_node was used, which couldn't distinguish between mutable references (actual JSON nodes from the original input) and immutable references (e.g. characters in a string). Yes, characters in a string are regarded as immutable because: * That's how it is in JavaScript. * It's easier to implement.
Diffstat (limited to 'jsonpath.c')
-rw-r--r--jsonpath.c152
1 files changed, 109 insertions, 43 deletions
diff --git a/jsonpath.c b/jsonpath.c
index a6ca56f..b8e4527 100644
--- a/jsonpath.c
+++ b/jsonpath.c
@@ -96,6 +96,28 @@ static jp_element *mkKeySubscript(char *key, size_t length, bool rd)
return elem;
}
+static JPRef *mkRef(JPRefType type)
+{
+ JPRef *ref = palloc0(sizeof(*ref));
+ ref->type = type;
+ return ref;
+}
+
+static JPRef *mkRefNode(json_node *node)
+{
+ JPRef *ref = mkRef(JP_REF_NODE);
+ ref->u.node = node;
+ return ref;
+}
+
+static JPRef *mkRefChar(const char *bytes, size_t length)
+{
+ JPRef *ref = mkRef(JP_REF_CHAR);
+ ref->u.chr.bytes = bytes;
+ ref->u.chr.length = length;
+ return ref;
+}
+
char *jp_show(JSONPath *jp)
{
StringInfoData string[1];
@@ -323,75 +345,104 @@ static json_node *json_head(json_node *parent)
#define json_foreach(child, parent) \
for ((child) = json_head(parent); (child) != NULL; (child) = (child)->next)
-static json_node *json_index_subscript(json_node *json, long index)
+static JPRef *json_index_subscript(JPRef *ref, long index)
{
+ json_node *json;
+
if (index < 0)
return NULL;
- switch (json->type) {
- case JSON_STRING: {
- const char *sub_start;
- size_t sub_bytes;
- size_t sub_length;
- json_node *sub_json;
-
- sub_length = utf8_substring(
- json->v.string.str, json->v.string.length,
- index, 1,
- &sub_start, &sub_bytes);
-
- if (sub_length != 1)
- return NULL;
+ switch (ref->type) {
+ case JP_REF_NODE:
+ json = ref->u.node;
- sub_json = json_mkstring(sub_start, sub_bytes);
- sub_json->parent = json;
- return sub_json;
- }
- case JSON_ARRAY: {
- json_node *child;
+ switch (json->type) {
+ case JSON_STRING: {
+ const char *sub_start;
+ size_t sub_bytes;
+ size_t sub_length;
- if ((size_t)index >= json->v.children.count)
- return NULL;
+ sub_length = utf8_substring(
+ json->v.string.str, json->v.string.length,
+ index, 1,
+ &sub_start, &sub_bytes);
- for (child = json->v.children.head;
- index && child;
- child = child->next, index--) {}
+ if (sub_length != 1)
+ return NULL;
- if (index != 0 || child == NULL) {
- Assert(false);
- return NULL;
+ return mkRefChar(sub_start, sub_bytes);
+ }
+ case JSON_ARRAY: {
+ json_node *child;
+
+ if ((size_t)index >= json->v.children.count)
+ return NULL;
+
+ for (child = json->v.children.head;
+ index && child;
+ child = child->next, index--) {}
+
+ if (index != 0 || child == NULL) {
+ Assert(false);
+ return NULL;
+ }
+
+ return mkRefNode(child);
+ }
+ default:
+ return NULL;
}
+ break;
+
+ case JP_REF_CHAR:
+ if (index != 0)
+ return NULL;
+ return ref;
- return child;
- }
default:
+ Assert(false);
return NULL;
}
}
-static void match_recurse(List **results, ListCell *path, json_node *json)
+/*
+Currently, a lot of JPRef nodes are allocated just to pass json_node pointers
+to match_recurse. If this becomes a memory/performance issue in the future,
+JPRef could merged with json_node by adding JPRef's specialty types to the
+json_type enum and json_node union. JPRef is currently not merged with
+json_node in an attempt to keep the codebase tidy and easier to extend.
+*/
+static void match_recurse(List **results, ListCell *path, JPRef *ref)
{
jp_element *elem;
- json_node *child;
+ JPRef *child_ref;
+ json_node *json, *child;
if (path == NULL) {
/* The end of the JSONPath list is the "accept" state. */
- *results = lappend(*results, json);
+ *results = lappend(*results, ref);
return;
}
elem = lfirst(path);
+ if (ref->type == JP_REF_NODE)
+ json = ref->u.node;
+ else
+ json = NULL;
+
switch (elem->type) {
case JP_WILDCARD:
- json_foreach(child, json)
- match_recurse(results, lnext(path), child);
+ if (json) {
+ json_foreach(child, json)
+ match_recurse(results, lnext(path), mkRefNode(child));
+ }
break;
case JP_INDEX_SUBSCRIPT:
- child = json_index_subscript(json, elem->data.index);
- if (child != NULL)
- match_recurse(results, lnext(path), child);
+ child_ref = json_index_subscript(ref, elem->data.index);
+ if (child_ref != NULL)
+ match_recurse(results, lnext(path), child_ref);
break;
case JP_KEY_SUBSCRIPT:
@@ -400,7 +451,7 @@ static void match_recurse(List **results, ListCell *path, json_node *json)
child->key_length == elem->data.key.length &&
!memcmp(child->key, elem->data.key.ptr, child->key_length))
{
- match_recurse(results, lnext(path), child);
+ match_recurse(results, lnext(path), mkRefNode(child));
}
}
break;
@@ -408,9 +459,9 @@ static void match_recurse(List **results, ListCell *path, json_node *json)
default:;
}
- if (elem->recursive_descent) {
+ if (elem->recursive_descent && json) {
json_foreach(child, json)
- match_recurse(results, path, child);
+ match_recurse(results, path, mkRefNode(child));
}
}
@@ -419,7 +470,22 @@ List *jp_match(JSONPath *jp, json_node *json)
ListCell *lc = jp_head(jp);
List *results = NIL;
- match_recurse(&results, lc, json);
+ match_recurse(&results, lc, mkRefNode(json));
return results;
}
+
+char *jpref_encode(JPRef *ref)
+{
+ switch (ref->type) {
+ case JP_REF_NODE:
+ return json_encode(ref->u.node, JSONOPT_USE_ORIG);
+
+ case JP_REF_CHAR:
+ return json_encode_string(ref->u.chr.bytes, ref->u.chr.length, '"', false);
+
+ default:
+ Assert(false);
+ return NULL;
+ }
+}