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