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