1 /* json-path.h - JSONPath implementation
2  *
3  * This file is part of JSON-GLib
4  * Copyright © 2011  Intel Corp.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Author:
20  *   Emmanuele Bassi  <ebassi@linux.intel.com>
21  */
22 
23 /**
24  * SECTION:json-path
25  * @Title: JsonPath
26  * @short_description: JSONPath implementation
27  *
28  * #JsonPath is a simple class implementing the JSONPath syntax for extracting
29  * data out of a JSON tree. While the semantics of the JSONPath expressions are
30  * heavily borrowed by the XPath specification for XML, the syntax follows the
31  * ECMAScript origins of JSON.
32  *
33  * Once a #JsonPath instance has been created, it has to compile a JSONPath
34  * expression using json_path_compile() before being able to match it to a
35  * JSON tree; the same #JsonPath instance can be used to match multiple JSON
36  * trees. It it also possible to compile a new JSONPath expression using the
37  * same #JsonPath instance; the previous expression will be discarded only if
38  * the compilation of the new expression is successful.
39  *
40  * The simple convenience function json_path_query() can be used for one-off
41  * matching.
42  *
43  * ## Syntax of the JSONPath expressions ##
44  *
45  * A JSONPath expression is composed by path indices and operators.
46  * Each path index can either be a member name or an element index inside
47  * a JSON tree. A JSONPath expression must start with the '$' operator; each
48  * path index is separated using either the dot notation or the bracket
49  * notation, e.g.:
50  *
51  * |[<!-- language="plain" -->
52  *   // dot notation
53  *   $.store.book[0].title
54  *
55  *   // bracket notation
56  *   $['store']['book'][0]['title']
57  * ]|
58  *
59  * The available operators are:
60  *
61  * * Root node
62  *   The `$` character represents the root node of the JSON tree, and
63  *   matches the entire document.
64  *
65  * * Child nodes can either be matched using `.` or `[]`. For instance,
66  *   both `$.store.book` and `$['store']['book']` match the contents of
67  *   the book member of the store object.
68  *
69  * * Child nodes can be reached without specifying the whole tree structure
70  *   through the recursive descent operator, or `..`. For instance,
71  *   `$..author` matches all author member in every object.
72  *
73  * * Child nodes can grouped through the wildcard operator, or `*`. For
74  *   instance, `$.store.book[*].author` matches all author members of any
75  *   object element contained in the book array of the store object.
76  *
77  * * Element nodes can be accessed using their index (starting from zero)
78  *   in the subscript operator `[]`. For instance, `$.store.book[0]` matches
79  *   the first element of the book array of the store object.
80  *
81  * * Subsets of element nodes can be accessed using the set notation
82  *   operator `[i,j,...]`. For instance, `$.store.book[0,2]` matches the
83  *   elements 0 and 2 (the first and third) of the book array of the store
84  *   object.
85  *
86  * * Slices of element nodes can be accessed using the slice notation
87  *   operation `[start:end:step]`. If start is omitted, the starting index
88  *   of the slice is implied to be zero; if end is omitted, the ending index
89  *   of the slice is implied to be the length of the array; if step is
90  *   omitted, the step of the slice is implied to be 1. For instance,
91  *   `$.store.book[:2]` matches the first two elements of the book array
92  *   of the store object.
93  *
94  * More information about JSONPath is available on Stefan Gössner's
95  * [JSONPath website](http://goessner.net/articles/JsonPath/).
96  *
97  * ## Example of JSONPath matches
98  * The following example shows some of the results of using #JsonPath
99  * on a JSON tree. We use the following JSON description of a bookstore:
100  *
101  * |[<!-- language="plain" -->
102  *   { "store": {
103  *       "book": [
104  *         { "category": "reference", "author": "Nigel Rees",
105  *           "title": "Sayings of the Century", "price": "8.95"  },
106  *         { "category": "fiction", "author": "Evelyn Waugh",
107  *           "title": "Sword of Honour", "price": "12.99" },
108  *         { "category": "fiction", "author": "Herman Melville",
109  *           "title": "Moby Dick", "isbn": "0-553-21311-3",
110  *           "price": "8.99" },
111  *         { "category": "fiction", "author": "J. R. R. Tolkien",
112  *           "title": "The Lord of the Rings", "isbn": "0-395-19395-8",
113  *           "price": "22.99" }
114  *       ],
115  *       "bicycle": { "color": "red", "price": "19.95" }
116  *     }
117  *   }
118  * ]|
119  *
120  * We can parse the JSON using #JsonParser:
121  *
122  * |[<!-- language="C" -->
123  *   JsonParser *parser = json_parser_new ();
124  *   json_parser_load_from_data (parser, json_data, -1, NULL);
125  * ]|
126  *
127  * If we run the following code:
128  *
129  * |[<!-- language="C" -->
130  *   JsonNode *result;
131  *   JsonPath *path = json_path_new ();
132  *   json_path_compile (path, "$.store..author", NULL);
133  *   result = json_path_match (path, json_parser_get_root (parser));
134  * ]|
135  *
136  * The result #JsonNode will contain an array with all values of the
137  * author member of the objects in the JSON tree. If we use a
138  * #JsonGenerator to convert the #JsonNode to a string and print it:
139  *
140  * |[<!-- language="C" -->
141  *   JsonGenerator *generator = json_generator_new ();
142  *   json_generator_set_root (generator, result);
143  *   char *str = json_generator_to_data (generator, NULL);
144  *   g_print ("Results: %s\n", str);
145  * ]|
146  *
147  * The output will be:
148  *
149  * |[<!-- language="plain" -->
150  *   ["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
151  * ]|
152  *
153  * #JsonPath is available since JSON-GLib 0.14
154  */
155 
156 #include "config.h"
157 
158 #include <string.h>
159 
160 #include <glib/gi18n-lib.h>
161 
162 #include "json-path.h"
163 
164 #include "json-debug.h"
165 #include "json-types-private.h"
166 
167 typedef enum {
168   JSON_PATH_NODE_ROOT,
169   JSON_PATH_NODE_CHILD_MEMBER,
170   JSON_PATH_NODE_CHILD_ELEMENT,
171   JSON_PATH_NODE_RECURSIVE_DESCENT,
172   JSON_PATH_NODE_WILDCARD_MEMBER,
173   JSON_PATH_NODE_WILDCARD_ELEMENT,
174   JSON_PATH_NODE_ELEMENT_SET,
175   JSON_PATH_NODE_ELEMENT_SLICE
176 } PathNodeType;
177 
178 typedef struct _PathNode        PathNode;
179 
180 struct _JsonPath
181 {
182   GObject parent_instance;
183 
184   /* the compiled path */
185   GList *nodes;
186 
187   guint is_compiled : 1;
188 };
189 
190 struct _JsonPathClass
191 {
192   GObjectClass parent_class;
193 };
194 
195 struct _PathNode
196 {
197   PathNodeType node_type;
198 
199   union {
200     /* JSON_PATH_NODE_CHILD_ELEMENT */
201     int element_index;
202 
203     /* JSON_PATH_NODE_CHILD_MEMBER */
204     char *member_name;
205 
206     /* JSON_PATH_NODE_ELEMENT_SET */
207     struct { int n_indices; int *indices; } set;
208 
209     /* JSON_PATH_NODE_ELEMENT_SLICE */
210     struct { int start, end, step; } slice;
211   } data;
212 };
213 
214 G_DEFINE_QUARK (json-path-error-quark, json_path_error)
215 
G_DEFINE_TYPE(JsonPath,json_path,G_TYPE_OBJECT)216 G_DEFINE_TYPE (JsonPath, json_path, G_TYPE_OBJECT)
217 
218 static void
219 path_node_free (gpointer data)
220 {
221   if (data != NULL)
222     {
223       PathNode *node = data;
224 
225       switch (node->node_type)
226         {
227         case JSON_PATH_NODE_CHILD_MEMBER:
228           g_free (node->data.member_name);
229           break;
230 
231         case JSON_PATH_NODE_ELEMENT_SET:
232           g_free (node->data.set.indices);
233           break;
234 
235         default:
236           break;
237         }
238 
239       g_free (node);
240     }
241 }
242 
243 static void
json_path_finalize(GObject * gobject)244 json_path_finalize (GObject *gobject)
245 {
246   JsonPath *self = JSON_PATH (gobject);
247 
248   g_list_free_full (self->nodes, path_node_free);
249 
250   G_OBJECT_CLASS (json_path_parent_class)->finalize (gobject);
251 }
252 
253 static void
json_path_class_init(JsonPathClass * klass)254 json_path_class_init (JsonPathClass *klass)
255 {
256   G_OBJECT_CLASS (klass)->finalize = json_path_finalize;
257 }
258 
259 static void
json_path_init(JsonPath * self)260 json_path_init (JsonPath *self)
261 {
262 }
263 
264 /**
265  * json_path_new:
266  *
267  * Creates a new #JsonPath instance.
268  *
269  * Once created, the #JsonPath object should be used with json_path_compile()
270  * and json_path_match().
271  *
272  * Return value: (transfer full): the newly created #JsonPath instance. Use
273  *   g_object_unref() to free the allocated resources when done
274  *
275  * Since: 0.14
276  */
277 JsonPath *
json_path_new(void)278 json_path_new (void)
279 {
280   return g_object_new (JSON_TYPE_PATH, NULL);
281 }
282 
283 #ifdef JSON_ENABLE_DEBUG
284 /* used as the function for a g_list_foreach() on a list of PathNode; needs
285  * a GString as the payload to build the output string
286  */
287 static void
json_path_foreach_print(gpointer data,gpointer user_data)288 json_path_foreach_print (gpointer data,
289                          gpointer user_data)
290 {
291   PathNode *cur_node = data;
292   GString *buf = user_data;
293 
294   switch (cur_node->node_type)
295     {
296     case JSON_PATH_NODE_ROOT:
297       g_string_append (buf, "<root");
298       break;
299 
300     case JSON_PATH_NODE_CHILD_MEMBER:
301       g_string_append_printf (buf, "<member '%s'", cur_node->data.member_name);
302       break;
303 
304     case JSON_PATH_NODE_CHILD_ELEMENT:
305       g_string_append_printf (buf, "<element '%d'", cur_node->data.element_index);
306       break;
307 
308     case JSON_PATH_NODE_RECURSIVE_DESCENT:
309       g_string_append (buf, "<recursive descent");
310       break;
311 
312     case JSON_PATH_NODE_WILDCARD_MEMBER:
313       g_string_append (buf, "<wildcard member");
314       break;
315 
316     case JSON_PATH_NODE_WILDCARD_ELEMENT:
317       g_string_append (buf, "<wildcard element");
318       break;
319 
320     case JSON_PATH_NODE_ELEMENT_SET:
321       {
322         int i;
323 
324         g_string_append (buf, "<element set ");
325         for (i = 0; i < cur_node->data.set.n_indices - 1; i++)
326           g_string_append_printf (buf, "'%d', ", cur_node->data.set.indices[i]);
327 
328         g_string_append_printf (buf, "'%d'", cur_node->data.set.indices[i]);
329       }
330       break;
331 
332     case JSON_PATH_NODE_ELEMENT_SLICE:
333       g_string_append_printf (buf, "<slice start '%d', end '%d', step '%d'",
334                               cur_node->data.slice.start,
335                               cur_node->data.slice.end,
336                               cur_node->data.slice.step);
337       break;
338 
339     default:
340       g_string_append (buf, "<unknown node");
341       break;
342     }
343 
344   g_string_append (buf, ">");
345 }
346 #endif /* JSON_ENABLE_DEBUG */
347 
348 /**
349  * json_path_compile:
350  * @path: a #JsonPath
351  * @expression: a JSONPath expression
352  * @error: return location for a #GError, or %NULL
353  *
354  * Validates and decomposes @expression.
355  *
356  * A JSONPath expression must be compiled before calling json_path_match().
357  *
358  * Return value: %TRUE on success; on error, @error will be set with
359  *   the %JSON_PATH_ERROR domain and a code from the #JsonPathError
360  *   enumeration, and %FALSE will be returned
361  *
362  * Since: 0.14
363  */
364 gboolean
json_path_compile(JsonPath * path,const char * expression,GError ** error)365 json_path_compile (JsonPath    *path,
366                    const char  *expression,
367                    GError     **error)
368 {
369   const char *p, *end_p;
370   PathNode *root = NULL;
371   GList *nodes = NULL;
372 
373   g_return_val_if_fail (expression != NULL, FALSE);
374 
375   p = expression;
376 
377   while (*p != '\0')
378     {
379       switch (*p)
380         {
381         case '$':
382           {
383             PathNode *node;
384 
385             if (root != NULL)
386               {
387                 g_set_error_literal (error, JSON_PATH_ERROR,
388                                      JSON_PATH_ERROR_INVALID_QUERY,
389                                      _("Only one root node is allowed in a JSONPath expression"));
390                 return FALSE;
391               }
392 
393             if (!(*(p + 1) == '.' || *(p + 1) == '[' || *(p + 1) == '\0'))
394               {
395                 g_set_error (error, JSON_PATH_ERROR,
396                              JSON_PATH_ERROR_INVALID_QUERY,
397                              /* translators: the %c is the invalid character */
398                              _("Root node followed by invalid character “%c”"),
399                              *(p + 1));
400                 return FALSE;
401               }
402 
403             node = g_new0 (PathNode, 1);
404             node->node_type = JSON_PATH_NODE_ROOT;
405 
406             root = node;
407             nodes = g_list_prepend (NULL, root);
408           }
409           break;
410 
411         case '.':
412         case '[':
413           {
414             PathNode *node = NULL;
415 
416             if (*p == '.' && *(p + 1) == '.')
417               {
418                 node = g_new0 (PathNode, 1);
419                 node->node_type = JSON_PATH_NODE_RECURSIVE_DESCENT;
420               }
421             else if (*p == '.' && *(p + 1) == '*')
422               {
423                 node = g_new0 (PathNode, 1);
424                 node->node_type = JSON_PATH_NODE_WILDCARD_MEMBER;
425 
426                 p += 1;
427               }
428             else if (*p == '.')
429               {
430                 end_p = p + 1;
431                 while (!(*end_p == '.' || *end_p == '[' || *end_p == '\0'))
432                   end_p += 1;
433 
434                 if (end_p == p + 1)
435                   {
436                     g_set_error_literal (error, JSON_PATH_ERROR,
437                                          JSON_PATH_ERROR_INVALID_QUERY,
438                                          _("Missing member name or wildcard after . character"));
439                     goto fail;
440                   }
441 
442                 node = g_new0 (PathNode, 1);
443                 node->node_type = JSON_PATH_NODE_CHILD_MEMBER;
444                 node->data.member_name = g_strndup (p + 1, end_p - p - 1);
445 
446                 p = end_p - 1;
447               }
448             else if (*p == '[' && *(p + 1) == '\'')
449               {
450                 if (*(p + 2) == '*' && *(p + 3) == '\'' && *(p + 4) == ']')
451                   {
452                     node = g_new0 (PathNode, 1);
453                     node->node_type = JSON_PATH_NODE_WILDCARD_MEMBER;
454 
455                     p += 4;
456                   }
457                 else
458                   {
459                     node = g_new0 (PathNode, 1);
460                     node->node_type = JSON_PATH_NODE_CHILD_MEMBER;
461 
462                     end_p = strchr (p + 2, '\'');
463                     node->data.member_name = g_strndup (p + 2, end_p - p - 2);
464 
465                     p = end_p + 1;
466                   }
467               }
468             else if (*p == '[' && *(p + 1) == '*' && *(p + 2) == ']')
469               {
470                 node = g_new0 (PathNode, 1);
471                 node->node_type = JSON_PATH_NODE_WILDCARD_ELEMENT;
472 
473                 p += 1;
474               }
475             else if (*p == '[')
476               {
477                 int sign = 1;
478                 int idx;
479 
480                 end_p = p + 1;
481 
482                 if (*end_p == '-')
483                   {
484                     sign = -1;
485                     end_p += 1;
486                   }
487 
488                 /* slice with missing start */
489                 if (*end_p == ':')
490                   {
491                     int slice_end = g_ascii_strtoll (end_p + 1, (char **) &end_p, 10) * sign;
492                     int slice_step = 1;
493 
494                     if (*end_p == ':')
495                       {
496                         end_p += 1;
497 
498                         if (*end_p == '-')
499                           {
500                             sign = -1;
501                             end_p += 1;
502                           }
503                         else
504                           sign = 1;
505 
506                         slice_step = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
507 
508                         if (*end_p != ']')
509                           {
510                             g_set_error (error, JSON_PATH_ERROR,
511                                          JSON_PATH_ERROR_INVALID_QUERY,
512                                          _("Malformed slice expression “%*s”"),
513                                          (int)(end_p - p),
514                                          p + 1);
515                             goto fail;
516                           }
517                       }
518 
519                     node = g_new0 (PathNode, 1);
520                     node->node_type = JSON_PATH_NODE_ELEMENT_SLICE;
521                     node->data.slice.start = 0;
522                     node->data.slice.end = slice_end;
523                     node->data.slice.step = slice_step;
524 
525                     nodes = g_list_prepend (nodes, node);
526                     p = end_p;
527                     break;
528                   }
529 
530                 idx = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
531 
532                 if (*end_p == ',')
533                   {
534                     GArray *indices = g_array_new (FALSE, TRUE, sizeof (int));
535 
536                     g_array_append_val (indices, idx);
537 
538                     while (*end_p != ']')
539                       {
540                         end_p += 1;
541 
542                         if (*end_p == '-')
543                           {
544                             sign = -1;
545                             end_p += 1;
546                           }
547                         else
548                           sign = 1;
549 
550                         idx = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
551                         if (!(*end_p == ',' || *end_p == ']'))
552                           {
553                             g_array_unref (indices);
554                             g_set_error (error, JSON_PATH_ERROR,
555                                          JSON_PATH_ERROR_INVALID_QUERY,
556                                          _("Invalid set definition “%*s”"),
557                                          (int)(end_p - p),
558                                          p + 1);
559                             goto fail;
560                           }
561 
562                         g_array_append_val (indices, idx);
563                       }
564 
565                     node = g_new0 (PathNode, 1);
566                     node->node_type = JSON_PATH_NODE_ELEMENT_SET;
567                     node->data.set.n_indices =  indices->len;
568                     node->data.set.indices = (int *) g_array_free (indices, FALSE);
569                     nodes = g_list_prepend (nodes, node);
570                     p = end_p;
571                     break;
572                   }
573                 else if (*end_p == ':')
574                   {
575                     int slice_start = idx;
576                     int slice_end = 0;
577                     int slice_step = 1;
578 
579                     end_p += 1;
580 
581                     if (*end_p == '-')
582                       {
583                         sign = -1;
584                         end_p += 1;
585                       }
586                     else
587                       sign = 1;
588 
589                     slice_end = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
590                     if (*end_p == ':')
591                       {
592                         end_p += 1;
593 
594                         if (*end_p == '-')
595                           {
596                             sign = -1;
597                             end_p += 1;
598                           }
599                         else
600                           sign = 1;
601 
602                         slice_step = g_ascii_strtoll (end_p + 1, (char **) &end_p, 10) * sign;
603                       }
604 
605                     if (*end_p != ']')
606                       {
607                         g_set_error (error, JSON_PATH_ERROR,
608                                      JSON_PATH_ERROR_INVALID_QUERY,
609                                      _("Invalid slice definition “%*s”"),
610                                      (int)(end_p - p),
611                                      p + 1);
612                         goto fail;
613                       }
614 
615                     node = g_new0 (PathNode, 1);
616                     node->node_type = JSON_PATH_NODE_ELEMENT_SLICE;
617                     node->data.slice.start = slice_start;
618                     node->data.slice.end = slice_end;
619                     node->data.slice.step = slice_step;
620                     nodes = g_list_prepend (nodes, node);
621                     p = end_p;
622                     break;
623                   }
624                 else if (*end_p == ']')
625                   {
626                     node = g_new0 (PathNode, 1);
627                     node->node_type = JSON_PATH_NODE_CHILD_ELEMENT;
628                     node->data.element_index = idx;
629                     nodes = g_list_prepend (nodes, node);
630                     p = end_p;
631                     break;
632                   }
633                 else
634                   {
635                     g_set_error (error, JSON_PATH_ERROR,
636                                  JSON_PATH_ERROR_INVALID_QUERY,
637                                  _("Invalid array index definition “%*s”"),
638                                  (int)(end_p - p),
639                                  p + 1);
640                     goto fail;
641                   }
642               }
643             else
644               break;
645 
646             if (node != NULL)
647               nodes = g_list_prepend (nodes, node);
648           }
649           break;
650 
651         default:
652           if (nodes == NULL)
653             {
654               g_set_error(error, JSON_PATH_ERROR,
655                           JSON_PATH_ERROR_INVALID_QUERY,
656                           _("Invalid first character “%c”"),
657                           *p);
658               return FALSE;
659             }
660           break;
661         }
662 
663       p += 1;
664     }
665 
666   nodes = g_list_reverse (nodes);
667 
668 #ifdef JSON_ENABLE_DEBUG
669   if (JSON_HAS_DEBUG (PATH))
670     {
671       GString *buf = g_string_new (NULL);
672 
673       g_list_foreach (nodes, json_path_foreach_print, buf);
674 
675       g_message ("[PATH] " G_STRLOC ": expression '%s' => '%s'", expression, buf->str);
676       g_string_free (buf, TRUE);
677     }
678 #endif /* JSON_ENABLE_DEBUG */
679 
680   g_list_free_full (path->nodes, path_node_free);
681 
682   path->nodes = nodes;
683   path->is_compiled = (path->nodes != NULL);
684 
685   return path->nodes != NULL;
686 
687 fail:
688   g_list_free_full (nodes, path_node_free);
689 
690   return FALSE;
691 }
692 
693 static void
walk_path_node(GList * path,JsonNode * root,JsonArray * results)694 walk_path_node (GList      *path,
695                 JsonNode   *root,
696                 JsonArray  *results)
697 {
698   PathNode *node = path->data;
699 
700   switch (node->node_type)
701     {
702     case JSON_PATH_NODE_ROOT:
703       if (path->next != NULL)
704           walk_path_node (path->next, root, results);
705       else
706           json_array_add_element (results, json_node_copy (root));
707       break;
708 
709     case JSON_PATH_NODE_CHILD_MEMBER:
710       if (JSON_NODE_HOLDS_OBJECT (root))
711         {
712           JsonObject *object = json_node_get_object (root);
713 
714           if (json_object_has_member (object, node->data.member_name))
715             {
716               JsonNode *member = json_object_get_member (object, node->data.member_name);
717 
718               if (path->next == NULL)
719                 {
720                   JSON_NOTE (PATH, "end of path at member '%s'", node->data.member_name);
721                   json_array_add_element (results, json_node_copy (member));
722                 }
723               else
724                 walk_path_node (path->next, member, results);
725             }
726         }
727       break;
728 
729     case JSON_PATH_NODE_CHILD_ELEMENT:
730       if (JSON_NODE_HOLDS_ARRAY (root))
731         {
732           JsonArray *array = json_node_get_array (root);
733 
734           if (json_array_get_length (array) >= node->data.element_index)
735             {
736               JsonNode *element = json_array_get_element (array, node->data.element_index);
737 
738               if (path->next == NULL)
739                 {
740                   JSON_NOTE (PATH, "end of path at element '%d'", node->data.element_index);
741                   json_array_add_element (results, json_node_copy (element));
742                 }
743               else
744                 walk_path_node (path->next, element, results);
745             }
746         }
747       break;
748 
749     case JSON_PATH_NODE_RECURSIVE_DESCENT:
750       {
751         PathNode *tmp = path->next->data;
752 
753         switch (json_node_get_node_type (root))
754           {
755           case JSON_NODE_OBJECT:
756             {
757               JsonObject *object = json_node_get_object (root);
758               GQueue *members = json_object_get_members_internal (object);
759               GList *l;
760 
761               for (l = members->head; l != NULL; l = l->next)
762                 {
763                   JsonNode *m = json_object_get_member (object, l->data);
764 
765                   if (tmp->node_type == JSON_PATH_NODE_CHILD_MEMBER &&
766                       strcmp (tmp->data.member_name, l->data) == 0)
767                     {
768                       JSON_NOTE (PATH, "entering '%s'", tmp->data.member_name);
769                       walk_path_node (path->next, root, results);
770                     }
771                   else
772                     {
773                       JSON_NOTE (PATH, "recursing into '%s'", (char *) l->data);
774                       walk_path_node (path, m, results);
775                     }
776                 }
777             }
778             break;
779 
780           case JSON_NODE_ARRAY:
781             {
782               JsonArray *array = json_node_get_array (root);
783               GList *members, *l;
784               int i;
785 
786               members = json_array_get_elements (array);
787               for (l = members, i = 0; l != NULL; l = l->next, i += 1)
788                 {
789                   JsonNode *m = l->data;
790 
791                   if (tmp->node_type == JSON_PATH_NODE_CHILD_ELEMENT &&
792                       tmp->data.element_index == i)
793                     {
794                       JSON_NOTE (PATH, "entering '%d'", tmp->data.element_index);
795                       walk_path_node (path->next, root, results);
796                     }
797                   else
798                     {
799                       JSON_NOTE (PATH, "recursing into '%d'", i);
800                       walk_path_node (path, m, results);
801                     }
802                 }
803               g_list_free (members);
804             }
805             break;
806 
807           default:
808             break;
809           }
810       }
811       break;
812 
813     case JSON_PATH_NODE_WILDCARD_MEMBER:
814       if (JSON_NODE_HOLDS_OBJECT (root))
815         {
816           JsonObject *object = json_node_get_object (root);
817           GQueue *members = json_object_get_members_internal (object);
818           GList *l;
819 
820           for (l = members->head; l != NULL; l = l->next)
821             {
822               JsonNode *member = json_object_get_member (object, l->data);
823 
824               if (path->next != NULL)
825                 walk_path_node (path->next, member, results);
826               else
827                 {
828                   JSON_NOTE (PATH, "glob match member '%s'", (char *) l->data);
829                   json_array_add_element (results, json_node_copy (member));
830                 }
831             }
832         }
833       else
834         json_array_add_element (results, json_node_copy (root));
835       break;
836 
837     case JSON_PATH_NODE_WILDCARD_ELEMENT:
838       if (JSON_NODE_HOLDS_ARRAY (root))
839         {
840           JsonArray *array = json_node_get_array (root);
841           GList *elements, *l;
842           int i;
843 
844           elements = json_array_get_elements (array);
845           for (l = elements, i = 0; l != NULL; l = l->next, i += 1)
846             {
847               JsonNode *element = l->data;
848 
849               if (path->next != NULL)
850                 walk_path_node (path->next, element, results);
851               else
852                 {
853                   JSON_NOTE (PATH, "glob match element '%d'", i);
854                   json_array_add_element (results, json_node_copy (element));
855                 }
856             }
857           g_list_free (elements);
858         }
859       else
860         json_array_add_element (results, json_node_copy (root));
861       break;
862 
863     case JSON_PATH_NODE_ELEMENT_SET:
864       if (JSON_NODE_HOLDS_ARRAY (root))
865         {
866           JsonArray *array = json_node_get_array (root);
867           int i;
868 
869           for (i = 0; i < node->data.set.n_indices; i += 1)
870             {
871               int idx = node->data.set.indices[i];
872               JsonNode *element = json_array_get_element (array, idx);
873 
874               if (path->next != NULL)
875                 walk_path_node (path->next, element, results);
876               else
877                 {
878                   JSON_NOTE (PATH, "set element '%d'", idx);
879                   json_array_add_element (results, json_node_copy (element));
880                 }
881             }
882         }
883       break;
884 
885     case JSON_PATH_NODE_ELEMENT_SLICE:
886       if (JSON_NODE_HOLDS_ARRAY (root))
887         {
888           JsonArray *array = json_node_get_array (root);
889           int i, start, end;
890 
891           if (node->data.slice.start < 0)
892             {
893               start = json_array_get_length (array)
894                     + node->data.slice.start;
895 
896               end = json_array_get_length (array)
897                   + node->data.slice.end;
898             }
899           else
900             {
901               start = node->data.slice.start;
902               end = node->data.slice.end;
903             }
904 
905           for (i = start; i < end; i += node->data.slice.step)
906             {
907               JsonNode *element = json_array_get_element (array, i);
908 
909               if (path->next != NULL)
910                 walk_path_node (path->next, element, results);
911               else
912                 {
913                   JSON_NOTE (PATH, "slice element '%d'", i);
914                   json_array_add_element (results, json_node_copy (element));
915                 }
916             }
917         }
918       break;
919 
920     default:
921       break;
922     }
923 }
924 
925 /**
926  * json_path_match:
927  * @path: a compiled #JsonPath
928  * @root: a #JsonNode
929  *
930  * Matches the JSON tree pointed by @root using the expression compiled
931  * into the #JsonPath.
932  *
933  * The matching #JsonNodes will be copied into a #JsonArray and
934  * returned wrapped in a #JsonNode.
935  *
936  * Return value: (transfer full): a newly-created #JsonNode of type
937  *   %JSON_NODE_ARRAY containing an array of matching #JsonNodes.
938  *   Use json_node_unref() when done
939  *
940  * Since: 0.14
941  */
942 JsonNode *
json_path_match(JsonPath * path,JsonNode * root)943 json_path_match (JsonPath *path,
944                  JsonNode *root)
945 {
946   JsonArray *results;
947   JsonNode *retval;
948 
949   g_return_val_if_fail (JSON_IS_PATH (path), NULL);
950   g_return_val_if_fail (path->is_compiled, NULL);
951   g_return_val_if_fail (root != NULL, NULL);
952 
953   results = json_array_new ();
954 
955   walk_path_node (path->nodes, root, results);
956 
957   retval = json_node_new (JSON_NODE_ARRAY);
958   json_node_take_array (retval, results);
959 
960   return retval;
961 }
962 
963 /**
964  * json_path_query:
965  * @expression: a JSONPath expression
966  * @root: the root of a JSON tree
967  * @error: return location for a #GError, or %NULL
968  *
969  * Queries a JSON tree using a JSONPath expression.
970  *
971  * This function is a simple wrapper around json_path_new(),
972  * json_path_compile() and json_path_match(). It implicitly
973  * creates a #JsonPath instance, compiles @expression and
974  * matches it against the JSON tree pointed by @root.
975  *
976  * Return value: (transfer full): a newly-created #JsonNode of type
977  *   %JSON_NODE_ARRAY containing an array of matching #JsonNodes.
978  *   Use json_node_unref() when done
979  *
980  * Since: 0.14
981  */
982 JsonNode *
json_path_query(const char * expression,JsonNode * root,GError ** error)983 json_path_query (const char  *expression,
984                  JsonNode    *root,
985                  GError     **error)
986 {
987   JsonPath *path = json_path_new ();
988   JsonNode *retval;
989 
990   if (!json_path_compile (path, expression, error))
991     {
992       g_object_unref (path);
993       return NULL;
994     }
995 
996   retval = json_path_match (path, root);
997 
998   g_object_unref (path);
999 
1000   return retval;
1001 }
1002