Imported Upstream version 1.2.4
[platform/upstream/json-glib.git] / json-glib / json-path.c
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  * |[
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 '[start,end]'. For instance, `$.store.book[0,2]` matches the
83  *   first, second, and third elements 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  *   { "store": {
102  *       "book": [
103  *         { "category": "reference", "author": "Nigel Rees",
104  *           "title": "Sayings of the Century", "price": "8.95"  },
105  *         { "category": "fiction", "author": "Evelyn Waugh",
106  *           "title": "Sword of Honour", "price": "12.99" },
107  *         { "category": "fiction", "author": "Herman Melville",
108  *           "title": "Moby Dick", "isbn": "0-553-21311-3",
109  *           "price": "8.99" },
110  *         { "category": "fiction", "author": "J. R. R. Tolkien",
111  *           "title": "The Lord of the Rings", "isbn": "0-395-19395-8",
112  *           "price": "22.99" }
113  *       ],
114  *       "bicycle": { "color": "red", "price": "19.95" }
115  *     }
116  *   }
117  * ]|
118  *
119  * We can parse the JSON using #JsonParser:
120  *
121  * |[<!-- language="C" -->
122  *   JsonParser *parser = json_parser_new ();
123  *   json_parser_load_from_data (parser, json_data, -1, NULL);
124  * ]|
125  *
126  * If we run the following code:
127  *
128  * |[<!-- language="C" -->
129  *   JsonNode *result;
130  *   JsonPath *path = json_path_new ();
131  *   json_path_compile (path, "$.store..author", NULL);
132  *   result = json_path_match (path, json_parser_get_root (parser));
133  * ]|
134  *
135  * The result #JsonNode will contain an array with all values of the
136  * author member of the objects in the JSON tree. If we use a
137  * #JsonGenerator to convert the #JsonNode to a string and print it:
138  *
139  * |[<!-- language="C" -->
140  *   JsonGenerator *generator = json_generator_new ();
141  *   json_generator_set_root (generator, result);
142  *   char *str = json_generator_to_data (generator, NULL);
143  *   g_print ("Results: %s\n", str);
144  * ]|
145  *
146  * The output will be:
147  *
148  * |[
149  *   ["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
150  * ]|
151  *
152  * #JsonPath is available since JSON-GLib 0.14
153  */
154
155 #include "config.h"
156
157 #include <string.h>
158
159 #include <glib/gi18n-lib.h>
160
161 #include "json-path.h"
162
163 #include "json-debug.h"
164 #include "json-types-private.h"
165
166 typedef enum {
167   JSON_PATH_NODE_ROOT,
168   JSON_PATH_NODE_CHILD_MEMBER,
169   JSON_PATH_NODE_CHILD_ELEMENT,
170   JSON_PATH_NODE_RECURSIVE_DESCENT,
171   JSON_PATH_NODE_WILDCARD_MEMBER,
172   JSON_PATH_NODE_WILDCARD_ELEMENT,
173   JSON_PATH_NODE_ELEMENT_SET,
174   JSON_PATH_NODE_ELEMENT_SLICE
175 } PathNodeType;
176
177 typedef struct _PathNode        PathNode;
178
179 struct _JsonPath
180 {
181   GObject parent_instance;
182
183   /* the compiled path */
184   GList *nodes;
185
186   guint is_compiled : 1;
187 };
188
189 struct _JsonPathClass
190 {
191   GObjectClass parent_class;
192 };
193
194 struct _PathNode
195 {
196   PathNodeType node_type;
197
198   union {
199     /* JSON_PATH_NODE_CHILD_ELEMENT */
200     int element_index;
201
202     /* JSON_PATH_NODE_CHILD_MEMBER */
203     char *member_name;
204
205     /* JSON_PATH_NODE_ELEMENT_SET */
206     struct { int n_indices; int *indices; } set;
207
208     /* JSON_PATH_NODE_ELEMENT_SLICE */
209     struct { int start, end, step; } slice;
210   } data;
211 };
212
213 G_DEFINE_QUARK (json-path-error-quark, json_path_error)
214
215 G_DEFINE_TYPE (JsonPath, json_path, G_TYPE_OBJECT)
216
217 static void
218 path_node_free (gpointer data)
219 {
220   if (data != NULL)
221     {
222       PathNode *node = data;
223
224       switch (node->node_type)
225         {
226         case JSON_PATH_NODE_CHILD_MEMBER:
227           g_free (node->data.member_name);
228           break;
229
230         case JSON_PATH_NODE_ELEMENT_SET:
231           g_free (node->data.set.indices);
232           break;
233
234         default:
235           break;
236         }
237
238       g_free (node);
239     }
240 }
241
242 static void
243 json_path_finalize (GObject *gobject)
244 {
245   JsonPath *self = JSON_PATH (gobject);
246
247   g_list_free_full (self->nodes, path_node_free);
248
249   G_OBJECT_CLASS (json_path_parent_class)->finalize (gobject);
250 }
251
252 static void
253 json_path_class_init (JsonPathClass *klass)
254 {
255   G_OBJECT_CLASS (klass)->finalize = json_path_finalize;
256 }
257
258 static void
259 json_path_init (JsonPath *self)
260 {
261 }
262
263 /**
264  * json_path_new:
265  *
266  * Creates a new #JsonPath instance.
267  *
268  * Once created, the #JsonPath object should be used with json_path_compile()
269  * and json_path_match().
270  *
271  * Return value: (transfer full): the newly created #JsonPath instance. Use
272  *   g_object_unref() to free the allocated resources when done
273  *
274  * Since: 0.14
275  */
276 JsonPath *
277 json_path_new (void)
278 {
279   return g_object_new (JSON_TYPE_PATH, NULL);
280 }
281
282 #ifdef JSON_ENABLE_DEBUG
283 /* used as the function for a g_list_foreach() on a list of PathNode; needs
284  * a GString as the payload to build the output string
285  */
286 static void
287 json_path_foreach_print (gpointer data,
288                          gpointer user_data)
289 {
290   PathNode *cur_node = data;
291   GString *buf = user_data;
292
293   switch (cur_node->node_type)
294     {
295     case JSON_PATH_NODE_ROOT:
296       g_string_append (buf, "<root");
297       break;
298
299     case JSON_PATH_NODE_CHILD_MEMBER:
300       g_string_append_printf (buf, "<member '%s'", cur_node->data.member_name);
301       break;
302
303     case JSON_PATH_NODE_CHILD_ELEMENT:
304       g_string_append_printf (buf, "<element '%d'", cur_node->data.element_index);
305       break;
306
307     case JSON_PATH_NODE_RECURSIVE_DESCENT:
308       g_string_append (buf, "<recursive descent");
309       break;
310
311     case JSON_PATH_NODE_WILDCARD_MEMBER:
312       g_string_append (buf, "<wildcard member");
313       break;
314
315     case JSON_PATH_NODE_WILDCARD_ELEMENT:
316       g_string_append (buf, "<wildcard element");
317       break;
318
319     case JSON_PATH_NODE_ELEMENT_SET:
320       {
321         int i;
322
323         g_string_append (buf, "<element set ");
324         for (i = 0; i < cur_node->data.set.n_indices - 1; i++)
325           g_string_append_printf (buf, "'%d', ", cur_node->data.set.indices[i]);
326
327         g_string_append_printf (buf, "'%d'", cur_node->data.set.indices[i]);
328       }
329       break;
330
331     case JSON_PATH_NODE_ELEMENT_SLICE:
332       g_string_append_printf (buf, "<slice start '%d', end '%d', step '%d'",
333                               cur_node->data.slice.start,
334                               cur_node->data.slice.end,
335                               cur_node->data.slice.step);
336       break;
337
338     default:
339       g_string_append (buf, "<unknown node");
340       break;
341     }
342
343   g_string_append (buf, ">");
344 }
345 #endif /* JSON_ENABLE_DEBUG */
346
347 /**
348  * json_path_compile:
349  * @path: a #JsonPath
350  * @expression: a JSONPath expression
351  * @error: return location for a #GError, or %NULL
352  *
353  * Validates and decomposes @expression.
354  *
355  * A JSONPath expression must be compiled before calling json_path_match().
356  *
357  * Return value: %TRUE on success; on error, @error will be set with
358  *   the %JSON_PATH_ERROR domain and a code from the #JsonPathError
359  *   enumeration, and %FALSE will be returned
360  *
361  * Since: 0.14
362  */
363 gboolean
364 json_path_compile (JsonPath    *path,
365                    const char  *expression,
366                    GError     **error)
367 {
368   const char *p, *end_p;
369   PathNode *root = NULL;
370   GList *nodes = NULL;
371
372   g_return_val_if_fail (expression != NULL, FALSE);
373
374   p = expression;
375
376   while (*p != '\0')
377     {
378       switch (*p)
379         {
380         case '$':
381           {
382             PathNode *node;
383
384             if (root != NULL)
385               {
386                 g_set_error_literal (error, JSON_PATH_ERROR,
387                                      JSON_PATH_ERROR_INVALID_QUERY,
388                                      _("Only one root node is allowed in a JSONPath expression"));
389                 return FALSE;
390               }
391
392             if (!(*(p + 1) == '.' || *(p + 1) == '[' || *(p + 1) == '\0'))
393               {
394                 g_set_error (error, JSON_PATH_ERROR,
395                              JSON_PATH_ERROR_INVALID_QUERY,
396                              /* translators: the %c is the invalid character */
397                              _("Root node followed by invalid character '%c'"),
398                              *(p + 1));
399                 return FALSE;
400               }
401             
402             node = g_new0 (PathNode, 1);
403             node->node_type = JSON_PATH_NODE_ROOT;
404
405             root = node;
406             nodes = g_list_prepend (NULL, root);
407           }
408           break;
409
410         case '.':
411         case '[':
412           {
413             PathNode *node = NULL;
414
415             if (*p == '.' && *(p + 1) == '.')
416               {
417                 node = g_new0 (PathNode, 1);
418                 node->node_type = JSON_PATH_NODE_RECURSIVE_DESCENT;
419               }
420             else if (*p == '.' && *(p + 1) == '*')
421               {
422                 node = g_new0 (PathNode, 1);
423                 node->node_type = JSON_PATH_NODE_WILDCARD_MEMBER;
424
425                 p += 1;
426               }
427             else if (*p == '.')
428               {
429                 end_p = p + 1;
430                 while (!(*end_p == '.' || *end_p == '[' || *end_p == '\0'))
431                   end_p += 1;
432
433                 if (end_p == p + 1)
434                   {
435                     g_set_error_literal (error, JSON_PATH_ERROR,
436                                          JSON_PATH_ERROR_INVALID_QUERY,
437                                          _("Missing member name or wildcard after . character"));
438                     goto fail;
439                   }
440
441                 node = g_new0 (PathNode, 1);
442                 node->node_type = JSON_PATH_NODE_CHILD_MEMBER;
443                 node->data.member_name = g_strndup (p + 1, end_p - p - 1);
444
445                 p = end_p - 1;
446               }
447             else if (*p == '[' && *(p + 1) == '\'')
448               {
449                 if (*(p + 2) == '*' && *(p + 3) == '\'' && *(p + 4) == ']')
450                   {
451                     node = g_new0 (PathNode, 1);
452                     node->node_type = JSON_PATH_NODE_WILDCARD_MEMBER;
453
454                     p += 4;
455                   }
456                 else
457                   {
458                     node = g_new0 (PathNode, 1);
459                     node->node_type = JSON_PATH_NODE_CHILD_MEMBER;
460
461                     end_p = strchr (p + 2, '\'');
462                     node->data.member_name = g_strndup (p + 2, end_p - p - 2);
463
464                     p = end_p + 1;
465                   }
466               }
467             else if (*p == '[' && *(p + 1) == '*' && *(p + 2) == ']')
468               {
469                 node = g_new0 (PathNode, 1);
470                 node->node_type = JSON_PATH_NODE_WILDCARD_ELEMENT;
471
472                 p += 1;
473               }
474             else if (*p == '[')
475               {
476                 int sign = 1;
477                 int idx;
478
479                 end_p = p + 1;
480
481                 if (*end_p == '-')
482                   {
483                     sign = -1;
484                     end_p += 1;
485                   }
486
487                 /* slice with missing start */
488                 if (*end_p == ':')
489                   {
490                     int slice_end = g_ascii_strtoll (end_p + 1, (char **) &end_p, 10) * sign;
491                     int slice_step = 1;
492
493                     if (*end_p == ':')
494                       {
495                         end_p += 1;
496
497                         if (*end_p == '-')
498                           {
499                             sign = -1;
500                             end_p += 1;
501                           }
502                         else
503                           sign = 1;
504
505                         slice_step = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
506
507                         if (*end_p != ']')
508                           {
509                             g_set_error (error, JSON_PATH_ERROR,
510                                          JSON_PATH_ERROR_INVALID_QUERY,
511                                          _("Malformed slice expression '%*s'"),
512                                          (int)(end_p - p),
513                                          p + 1);
514                             goto fail;
515                           }
516                       }
517
518                     node = g_new0 (PathNode, 1);
519                     node->node_type = JSON_PATH_NODE_ELEMENT_SLICE;
520                     node->data.slice.start = 0;
521                     node->data.slice.end = slice_end;
522                     node->data.slice.step = slice_step;
523
524                     nodes = g_list_prepend (nodes, node);
525                     p = end_p;
526                     break;
527                   }
528
529                 idx = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
530
531                 if (*end_p == ',')
532                   {
533                     GArray *indices = g_array_new (FALSE, TRUE, sizeof (int));
534
535                     g_array_append_val (indices, idx);
536
537                     while (*end_p != ']')
538                       {
539                         end_p += 1;
540
541                         if (*end_p == '-')
542                           {
543                             sign = -1;
544                             end_p += 1;
545                           }
546                         else
547                           sign = 1;
548
549                         idx = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
550                         if (!(*end_p == ',' || *end_p == ']'))
551                           {
552                             g_array_unref (indices);
553                             g_set_error (error, JSON_PATH_ERROR,
554                                          JSON_PATH_ERROR_INVALID_QUERY,
555                                          _("Invalid set definition '%*s'"),
556                                          (int)(end_p - p),
557                                          p + 1);
558                             goto fail;
559                           }
560
561                         g_array_append_val (indices, idx);
562                       }
563
564                     node = g_new0 (PathNode, 1);
565                     node->node_type = JSON_PATH_NODE_ELEMENT_SET;
566                     node->data.set.n_indices =  indices->len;
567                     node->data.set.indices = (int *) g_array_free (indices, FALSE);
568                     nodes = g_list_prepend (nodes, node);
569                     p = end_p;
570                     break;
571                   }
572                 else if (*end_p == ':')
573                   {
574                     int slice_start = idx;
575                     int slice_end = 0;
576                     int slice_step = 1;
577
578                     end_p += 1;
579
580                     if (*end_p == '-')
581                       {
582                         sign = -1;
583                         end_p += 1;
584                       }
585                     else
586                       sign = 1;
587
588                     slice_end = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
589                     if (*end_p == ':')
590                       {
591                         end_p += 1;
592
593                         if (*end_p == '-')
594                           {
595                             sign = -1;
596                             end_p += 1;
597                           }
598                         else
599                           sign = 1;
600
601                         slice_step = g_ascii_strtoll (end_p + 1, (char **) &end_p, 10) * sign;
602                       }
603
604                     if (*end_p != ']')
605                       {
606                         g_set_error (error, JSON_PATH_ERROR,
607                                      JSON_PATH_ERROR_INVALID_QUERY,
608                                      _("Invalid slice definition '%*s'"),
609                                      (int)(end_p - p),
610                                      p + 1);
611                         goto fail;
612                       }
613
614                     node = g_new0 (PathNode, 1);
615                     node->node_type = JSON_PATH_NODE_ELEMENT_SLICE;
616                     node->data.slice.start = slice_start;
617                     node->data.slice.end = slice_end;
618                     node->data.slice.step = slice_step;
619                     nodes = g_list_prepend (nodes, node);
620                     p = end_p;
621                     break;
622                   }
623                 else if (*end_p == ']')
624                   {
625                     node = g_new0 (PathNode, 1);
626                     node->node_type = JSON_PATH_NODE_CHILD_ELEMENT;
627                     node->data.element_index = idx;
628                     nodes = g_list_prepend (nodes, node);
629                     p = end_p;
630                     break;
631                   }
632                 else
633                   {
634                     g_set_error (error, JSON_PATH_ERROR,
635                                  JSON_PATH_ERROR_INVALID_QUERY,
636                                  _("Invalid array index definition '%*s'"),
637                                  (int)(end_p - p),
638                                  p + 1);
639                     goto fail;
640                   }
641               }
642             else
643               break;
644
645             if (node != NULL)
646               nodes = g_list_prepend (nodes, node);
647           }
648           break;
649
650         default:
651           if (nodes == NULL)
652             {
653               g_set_error(error, JSON_PATH_ERROR,
654                           JSON_PATH_ERROR_INVALID_QUERY,
655                           _("Invalid first character '%c'"),
656                           *p);
657               return FALSE;
658             }
659           break;
660         }
661
662       p += 1;
663     }
664
665   nodes = g_list_reverse (nodes);
666
667 #ifdef JSON_ENABLE_DEBUG
668   if (JSON_HAS_DEBUG (PATH))
669     {
670       GString *buf = g_string_new (NULL);
671
672       g_list_foreach (nodes, json_path_foreach_print, buf);
673
674       g_message ("[PATH] " G_STRLOC ": expression '%s' => '%s'", expression, buf->str);
675       g_string_free (buf, TRUE);
676     }
677 #endif /* JSON_ENABLE_DEBUG */
678
679   g_list_free_full (path->nodes, path_node_free);
680
681   path->nodes = nodes;
682   path->is_compiled = (path->nodes != NULL);
683
684   return path->nodes != NULL;
685
686 fail:
687   g_list_free_full (nodes, path_node_free);
688
689   return FALSE;
690 }
691
692 static void
693 walk_path_node (GList      *path,
694                 JsonNode   *root,
695                 JsonArray  *results)
696 {
697   PathNode *node = path->data;
698
699   switch (node->node_type)
700     {
701     case JSON_PATH_NODE_ROOT:
702       if (path->next != NULL)
703           walk_path_node (path->next, root, results);
704       else
705           json_array_add_element (results, json_node_copy (root));
706       break;
707
708     case JSON_PATH_NODE_CHILD_MEMBER:
709       if (JSON_NODE_HOLDS_OBJECT (root))
710         {
711           JsonObject *object = json_node_get_object (root);
712
713           if (json_object_has_member (object, node->data.member_name))
714             {
715               JsonNode *member = json_object_get_member (object, node->data.member_name);
716               
717               if (path->next == NULL)
718                 {
719                   JSON_NOTE (PATH, "end of path at member '%s'", node->data.member_name);
720                   json_array_add_element (results, json_node_copy (member));
721                 }
722               else
723                 walk_path_node (path->next, member, results);
724             }
725         }
726       break;
727
728     case JSON_PATH_NODE_CHILD_ELEMENT:
729       if (JSON_NODE_HOLDS_ARRAY (root))
730         {
731           JsonArray *array = json_node_get_array (root);
732
733           if (json_array_get_length (array) >= node->data.element_index)
734             {
735               JsonNode *element = json_array_get_element (array, node->data.element_index);
736
737               if (path->next == NULL)
738                 {
739                   JSON_NOTE (PATH, "end of path at element '%d'", node->data.element_index);
740                   json_array_add_element (results, json_node_copy (element));
741                 }
742               else
743                 walk_path_node (path->next, element, results);
744             }
745         }
746       break;
747
748     case JSON_PATH_NODE_RECURSIVE_DESCENT:
749       {
750         PathNode *tmp = path->next->data;
751
752         switch (json_node_get_node_type (root))
753           {
754           case JSON_NODE_OBJECT:
755             {
756               JsonObject *object = json_node_get_object (root);
757               GList *members, *l;
758
759               members = json_object_get_members (object);
760               for (l = members; l != NULL; l = l->next)
761                 {
762                   JsonNode *m = json_object_get_member (object, l->data);
763
764                   if (tmp->node_type == JSON_PATH_NODE_CHILD_MEMBER &&
765                       strcmp (tmp->data.member_name, l->data) == 0)
766                     {
767                       JSON_NOTE (PATH, "entering '%s'", tmp->data.member_name);
768                       walk_path_node (path->next, root, results);
769                     }
770                   else
771                     {
772                       JSON_NOTE (PATH, "recursing into '%s'", (char *) l->data);
773                       walk_path_node (path, m, results);
774                     }
775                 }
776               g_list_free (members);
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           GList *members, *l;
818
819           members = json_object_get_members (object);
820           for (l = members; 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           g_list_free (members);
833         }
834       else
835         json_array_add_element (results, json_node_copy (root));
836       break;
837
838     case JSON_PATH_NODE_WILDCARD_ELEMENT:
839       if (JSON_NODE_HOLDS_ARRAY (root))
840         {
841           JsonArray *array = json_node_get_array (root);
842           GList *elements, *l;
843           int i;
844
845           elements = json_array_get_elements (array);
846           for (l = elements, i = 0; l != NULL; l = l->next, i += 1)
847             {
848               JsonNode *element = l->data;
849
850               if (path->next != NULL)
851                 walk_path_node (path->next, element, results);
852               else
853                 {
854                   JSON_NOTE (PATH, "glob match element '%d'", i);
855                   json_array_add_element (results, json_node_copy (element));
856                 }
857             }
858           g_list_free (elements);
859         }
860       else
861         json_array_add_element (results, json_node_copy (root));
862       break;
863
864     case JSON_PATH_NODE_ELEMENT_SET:
865       if (JSON_NODE_HOLDS_ARRAY (root))
866         {
867           JsonArray *array = json_node_get_array (root);
868           int i;
869
870           for (i = 0; i < node->data.set.n_indices; i += 1)
871             {
872               int idx = node->data.set.indices[i];
873               JsonNode *element = json_array_get_element (array, idx);
874
875               if (path->next != NULL)
876                 walk_path_node (path->next, element, results);
877               else
878                 {
879                   JSON_NOTE (PATH, "set element '%d'", idx);
880                   json_array_add_element (results, json_node_copy (element));
881                 }
882             }
883         }
884       break;
885
886     case JSON_PATH_NODE_ELEMENT_SLICE:
887       if (JSON_NODE_HOLDS_ARRAY (root))
888         {
889           JsonArray *array = json_node_get_array (root);
890           int i, start, end;
891
892           if (node->data.slice.start < 0)
893             {
894               start = json_array_get_length (array)
895                     + node->data.slice.start;
896
897               end = json_array_get_length (array)
898                   + node->data.slice.end;
899             }
900           else
901             {
902               start = node->data.slice.start;
903               end = node->data.slice.end;
904             }
905
906           for (i = start; i < end; i += node->data.slice.step)
907             {
908               JsonNode *element = json_array_get_element (array, i);
909
910               if (path->next != NULL)
911                 walk_path_node (path->next, element, results);
912               else
913                 {
914                   JSON_NOTE (PATH, "slice element '%d'", i);
915                   json_array_add_element (results, json_node_copy (element));
916                 }
917             }
918         }
919       break;
920
921     default:
922       break;
923     }
924 }
925
926 /**
927  * json_path_match:
928  * @path: a compiled #JsonPath
929  * @root: a #JsonNode
930  *
931  * Matches the JSON tree pointed by @root using the expression compiled
932  * into the #JsonPath.
933  *
934  * The matching #JsonNodes will be copied into a #JsonArray and
935  * returned wrapped in a #JsonNode.
936  *
937  * Return value: (transfer full): a newly-created #JsonNode of type
938  *   %JSON_NODE_ARRAY containing an array of matching #JsonNodes.
939  *   Use json_node_unref() when done
940  *
941  * Since: 0.14
942  */
943 JsonNode *
944 json_path_match (JsonPath *path,
945                  JsonNode *root)
946 {
947   JsonArray *results;
948   JsonNode *retval;
949
950   g_return_val_if_fail (JSON_IS_PATH (path), NULL);
951   g_return_val_if_fail (path->is_compiled, NULL);
952   g_return_val_if_fail (root != NULL, NULL);
953
954   results = json_array_new ();
955
956   walk_path_node (path->nodes, root, results);
957
958   retval = json_node_new (JSON_NODE_ARRAY);
959   json_node_take_array (retval, results);
960
961   return retval;
962 }
963
964 /**
965  * json_path_query:
966  * @expression: a JSONPath expression
967  * @root: the root of a JSON tree
968  * @error: return location for a #GError, or %NULL
969  *
970  * Queries a JSON tree using a JSONPath expression.
971  *
972  * This function is a simple wrapper around json_path_new(),
973  * json_path_compile() and json_path_match(). It implicitly
974  * creates a #JsonPath instance, compiles @expression and
975  * matches it against the JSON tree pointed by @root.
976  *
977  * Return value: (transfer full): a newly-created #JsonNode of type
978  *   %JSON_NODE_ARRAY containing an array of matching #JsonNodes.
979  *   Use json_node_unref() when done
980  *
981  * Since: 0.14
982  */
983 JsonNode *
984 json_path_query (const char  *expression,
985                  JsonNode    *root,
986                  GError     **error)
987 {
988   JsonPath *path = json_path_new ();
989   JsonNode *retval;
990
991   if (!json_path_compile (path, expression, error))
992     {
993       g_object_unref (path);
994       return NULL;
995     }
996
997   retval = json_path_match (path, root);
998
999   g_object_unref (path);
1000
1001   return retval;
1002 }