gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] branch master updated: add parser for json path


From: gnunet
Subject: [taler-exchange] branch master updated: add parser for json path
Date: Tue, 21 Jul 2020 09:15:04 +0200

This is an automated email from the git hooks/post-receive script.

jonathan-buchanan pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new fecfa277 add parser for json path
fecfa277 is described below

commit fecfa277279c4f93a844de7cad1e75749d2c4f9f
Author: Jonathan Buchanan <jonathan.russ.buchanan@gmail.com>
AuthorDate: Tue Jul 21 03:14:41 2020 -0400

    add parser for json path
---
 src/include/taler_json_lib.h |  32 ++++++++++
 src/json/json.c              | 143 ++++++++++++++++++++++++++++++++++++++++++
 src/json/test_json.c         | 144 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 319 insertions(+)

diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index cfab56fd..08b72b5b 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -183,6 +183,38 @@ TALER_JSON_contract_part_forget (json_t *json,
                                  const char *field);
 
 
+/**
+ * Called for each path found after expanding a path.
+ *
+ * @param cls the closure.
+ * @param object_id the name of the object that is pointed to.
+ * @param parent the parent of the object at @e object_id.
+ */
+typedef void
+(*TALER_JSON_ExpandPathCallback) (
+  void *cls,
+  const char *object_id,
+  json_t *parent);
+
+
+/**
+ * Expands a path for a json object. May call the callback several times
+ * if the path contains a wildcard.
+ *
+ * @param json the json object the path references.
+ * @param path the path to expand. Must begin with "$." and follow dot 
notation,
+ *        and may include array indices and wildcards.
+ * @param cb the callback.
+ * @param cb_cls closure for the callback.
+ * @return GNUNET_OK on success, GNUNET_SYSERR if @e path is invalid.
+ */
+int
+TALER_JSON_expand_path (json_t *json,
+                        const char *path,
+                        TALER_JSON_ExpandPathCallback cb,
+                        void *cb_cls);
+
+
 /**
  * Extract the Taler error code from the given @a json object.
  * Note that #TALER_EC_NONE is returned if no "code" is present.
diff --git a/src/json/json.c b/src/json/json.c
index 9ee1628e..bcae6de1 100644
--- a/src/json/json.c
+++ b/src/json/json.c
@@ -459,6 +459,149 @@ TALER_JSON_contract_part_forget (json_t *json,
 }
 
 
+/**
+ * Parse a json path.
+ *
+ * @param obj the object that the path is relative to.
+ * @param prev the parent of @e obj.
+ * @param path the path to parse.
+ * @param cb the callback to call, if we get to the end of @e path.
+ * @param cb_cls the closure for the callback.
+ * @return GNUNET_OK on success, GNUNET_SYSERR if @e path is malformed.
+ */
+static int
+parse_path (json_t *obj,
+            json_t *prev,
+            const char *path,
+            TALER_JSON_ExpandPathCallback cb,
+            void *cb_cls)
+{
+  char *id = GNUNET_strdup (path);
+  char *next_id = strchr (id,
+                          '.');
+  char *next_path;
+  char *bracket;
+  json_t *parent = obj;
+  json_t *next_obj = NULL;
+
+  if (NULL != next_id)
+  {
+    bracket = strchr (next_id,
+                      '[');
+    *next_id = '\0';
+    next_id++;
+    next_path = GNUNET_strdup (next_id);
+    char *next_dot = strchr (next_id,
+                             '.');
+    if (NULL != next_dot)
+      *next_dot = '\0';
+  }
+  else
+  {
+    cb (cb_cls,
+        id,
+        prev);
+    return GNUNET_OK;
+  }
+
+  /* If this is the first time this is called, make sure id is "$" */
+  if ((NULL == prev) &&
+      (0 != strcmp (id,
+                    "$")))
+    return GNUNET_SYSERR;
+
+  /* Check for bracketed indices */
+  if (NULL != bracket)
+  {
+    char *end_bracket = strchr (bracket,
+                                ']');
+    if (NULL == end_bracket)
+      return GNUNET_SYSERR;
+    *end_bracket = '\0';
+
+    *bracket = '\0';
+    bracket++;
+
+    parent = json_object_get (obj,
+                              next_id);
+    if (0 == strcmp (bracket,
+                     "*"))
+    {
+      size_t index;
+      json_t *value;
+      int ret = GNUNET_OK;
+      json_array_foreach (parent, index, value) {
+        ret = parse_path (value,
+                          obj,
+                          next_path,
+                          cb,
+                          cb_cls);
+        if (GNUNET_OK != ret)
+        {
+          GNUNET_free (id);
+          return ret;
+        }
+      }
+    }
+    else
+    {
+      unsigned int index;
+      if (1 != sscanf (bracket,
+                       "%u",
+                       &index))
+        return GNUNET_SYSERR;
+      next_obj = json_array_get (parent,
+                                 index);
+    }
+  }
+  else
+  {
+    /* No brackets, so just fetch the object by name */
+    next_obj = json_object_get (obj,
+                                next_id);
+  }
+
+  if (NULL != next_obj)
+  {
+    return parse_path (next_obj,
+                       parent,
+                       next_path,
+                       cb,
+                       cb_cls);
+  }
+
+  GNUNET_free (id);
+  GNUNET_free (next_path);
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Expands a path for a json object. May call the callback several times
+ * if the path contains a wildcard.
+ *
+ * @param json the json object the path references.
+ * @param path the path to expand. Must begin with "$." and follow dot 
notation,
+ *        and may include array indices and wildcards.
+ * @param cb the callback.
+ * @param cb_cls closure for the callback.
+ * @return GNUNET_OK on success, GNUNET_SYSERR if @e path is invalid.
+ */
+int
+TALER_JSON_expand_path (json_t *json,
+                        const char *path,
+                        TALER_JSON_ExpandPathCallback cb,
+                        void *cb_cls)
+{
+  return parse_path (json,
+                     NULL,
+                     path,
+                     cb,
+                     cb_cls);
+}
+
+
 /**
  * Extract the Taler error code from the given @a json object.
  * Note that #TALER_EC_NONE is returned if no "code" is present.
diff --git a/src/json/test_json.c b/src/json/test_json.c
index e876d95d..a9947520 100644
--- a/src/json/test_json.c
+++ b/src/json/test_json.c
@@ -56,6 +56,36 @@ test_amount (void)
 }
 
 
+struct TestPath_Closure
+{
+  const char **object_ids;
+
+  const json_t **parents;
+
+  unsigned int results_length;
+
+  int cmp_result;
+};
+
+
+static void
+path_cb (void *cls,
+         const char *object_id,
+         json_t *parent)
+{
+  struct TestPath_Closure *cmp = cls;
+  if (NULL == cmp)
+    return;
+  unsigned int i = cmp->results_length;
+  if ((0 != strcmp (cmp->object_ids[i],
+                    object_id)) ||
+      (1 != json_equal (cmp->parents[i],
+                        parent)))
+    cmp->cmp_result = 1;
+  cmp->results_length += 1;
+}
+
+
 static int
 test_contract ()
 {
@@ -64,6 +94,7 @@ test_contract ()
   json_t *c1;
   json_t *c2;
   json_t *c3;
+  json_t *c4;
 
   c1 = json_pack ("{s:s, s:{s:s, s:{s:s}}}",
                   "k1", "v1",
@@ -113,6 +144,119 @@ test_contract ()
                  TALER_JSON_contract_hash (c3,
                                            &h2));
   json_decref (c3);
+  c4 = json_pack ("{s:{s:s}, s:[{s:s}, {s:s}, {s:s}]}",
+                  "abc1",
+                  "xyz", "value",
+                  "fruit",
+                  "name", "banana",
+                  "name", "apple",
+                  "name", "orange");
+  GNUNET_assert (NULL != c4);
+  GNUNET_assert (GNUNET_SYSERR ==
+                 TALER_JSON_expand_path (c4,
+                                         "%.xyz",
+                                         &path_cb,
+                                         NULL));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_JSON_expand_path (c4,
+                                         "$.nonexistent_id",
+                                         &path_cb,
+                                         NULL));
+  GNUNET_assert (GNUNET_SYSERR ==
+                 TALER_JSON_expand_path (c4,
+                                         "$.fruit[n]",
+                                         &path_cb,
+                                         NULL));
+
+  {
+    const char *object_ids[] = { "xyz" };
+    const json_t *parents[] = {
+      json_object_get (c4,
+                       "abc1")
+    };
+    struct TestPath_Closure tp = {
+      .object_ids = object_ids,
+      .parents = parents,
+      .results_length = 0,
+      .cmp_result = 0
+    };
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_JSON_expand_path (c4,
+                                           "$.abc1.xyz",
+                                           &path_cb,
+                                           &tp));
+    GNUNET_assert (1 == tp.results_length);
+    GNUNET_assert (0 == tp.cmp_result);
+  }
+  {
+    const char *object_ids[] = { "name" };
+    const json_t *parents[] = {
+      json_array_get (json_object_get (c4,
+                                       "fruit"),
+                      0)
+    };
+    struct TestPath_Closure tp = {
+      .object_ids = object_ids,
+      .parents = parents,
+      .results_length = 0,
+      .cmp_result = 0
+    };
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_JSON_expand_path (c4,
+                                           "$.fruit[0].name",
+                                           &path_cb,
+                                           &tp));
+    GNUNET_assert (1 == tp.results_length);
+    GNUNET_assert (0 == tp.cmp_result);
+  }
+  {
+    const char *object_ids[] = { "name", "name", "name" };
+    const json_t *parents[] = {
+      json_array_get (json_object_get (c4,
+                                       "fruit"),
+                      0),
+      json_array_get (json_object_get (c4,
+                                       "fruit"),
+                      1),
+      json_array_get (json_object_get (c4,
+                                       "fruit"),
+                      2)
+    };
+    struct TestPath_Closure tp = {
+      .object_ids = object_ids,
+      .parents = parents,
+      .results_length = 0,
+      .cmp_result = 0
+    };
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_JSON_expand_path (c4,
+                                           "$.fruit[*].name",
+                                           &path_cb,
+                                           &tp));
+    GNUNET_assert (3 == tp.results_length);
+    GNUNET_assert (0 == tp.cmp_result);
+  }
+  {
+    const char *object_ids[] = { "fruit[0]" };
+    const json_t *parents[] = {
+      json_object_get (c4,
+                       "fruit")
+    };
+    struct TestPath_Closure tp = {
+      .object_ids = object_ids,
+      .parents = parents,
+      .results_length = 0,
+      .cmp_result = 0
+    };
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_JSON_expand_path (c4,
+                                           "$.fruit[0]",
+                                           &path_cb,
+                                           &tp));
+    GNUNET_assert (1 == tp.results_length);
+    GNUNET_assert (0 == tp.cmp_result);
+  }
+  json_decref (c4);
   if (0 !=
       GNUNET_memcmp (&h1,
                      &h2))

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]