|
Packit |
4c4d6b |
/* json-path.h - JSONPath implementation
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* This file is part of JSON-GLib
|
|
Packit |
4c4d6b |
* Copyright © 2011 Intel Corp.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* This library is free software; you can redistribute it and/or
|
|
Packit |
4c4d6b |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
4c4d6b |
* License as published by the Free Software Foundation; either
|
|
Packit |
4c4d6b |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
4c4d6b |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
4c4d6b |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
4c4d6b |
* Lesser General Public License for more details.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
4c4d6b |
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Author:
|
|
Packit |
4c4d6b |
* Emmanuele Bassi <ebassi@linux.intel.com>
|
|
Packit |
4c4d6b |
*/
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
/**
|
|
Packit |
4c4d6b |
* SECTION:json-path
|
|
Packit |
4c4d6b |
* @Title: JsonPath
|
|
Packit |
4c4d6b |
* @short_description: JSONPath implementation
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* #JsonPath is a simple class implementing the JSONPath syntax for extracting
|
|
Packit |
4c4d6b |
* data out of a JSON tree. While the semantics of the JSONPath expressions are
|
|
Packit |
4c4d6b |
* heavily borrowed by the XPath specification for XML, the syntax follows the
|
|
Packit |
4c4d6b |
* ECMAScript origins of JSON.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Once a #JsonPath instance has been created, it has to compile a JSONPath
|
|
Packit |
4c4d6b |
* expression using json_path_compile() before being able to match it to a
|
|
Packit |
4c4d6b |
* JSON tree; the same #JsonPath instance can be used to match multiple JSON
|
|
Packit |
4c4d6b |
* trees. It it also possible to compile a new JSONPath expression using the
|
|
Packit |
4c4d6b |
* same #JsonPath instance; the previous expression will be discarded only if
|
|
Packit |
4c4d6b |
* the compilation of the new expression is successful.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* The simple convenience function json_path_query() can be used for one-off
|
|
Packit |
4c4d6b |
* matching.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* ## Syntax of the JSONPath expressions ##
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* A JSONPath expression is composed by path indices and operators.
|
|
Packit |
4c4d6b |
* Each path index can either be a member name or an element index inside
|
|
Packit |
4c4d6b |
* a JSON tree. A JSONPath expression must start with the '$' operator; each
|
|
Packit |
4c4d6b |
* path index is separated using either the dot notation or the bracket
|
|
Packit |
4c4d6b |
* notation, e.g.:
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* |[
|
|
Packit |
4c4d6b |
* // dot notation
|
|
Packit |
4c4d6b |
* $.store.book[0].title
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* // bracket notation
|
|
Packit |
4c4d6b |
* $['store']['book'][0]['title']
|
|
Packit |
4c4d6b |
* ]|
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* The available operators are:
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* * Root node
|
|
Packit |
4c4d6b |
* The `$` character represents the root node of the JSON tree, and
|
|
Packit |
4c4d6b |
* matches the entire document.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* * Child nodes can either be matched using `.` or `[]`. For instance,
|
|
Packit |
4c4d6b |
* both `$.store.book` and `$['store']['book']` match the contents of
|
|
Packit |
4c4d6b |
* the book member of the store object.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* * Child nodes can be reached without specifying the whole tree structure
|
|
Packit |
4c4d6b |
* through the recursive descent operator, or `..`. For instance,
|
|
Packit |
4c4d6b |
* `$..author` matches all author member in every object.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* * Child nodes can grouped through the wildcard operator, or `*`. For
|
|
Packit |
4c4d6b |
* instance, `$.store.book[*].author` matches all author members of any
|
|
Packit |
4c4d6b |
* object element contained in the book array of the store object.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* * Element nodes can be accessed using their index (starting from zero)
|
|
Packit |
4c4d6b |
* in the subscript operator `[]`. For instance, `$.store.book[0]` matches
|
|
Packit |
4c4d6b |
* the first element of the book array of the store object.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* * Subsets of element nodes can be accessed using the set notation
|
|
Packit |
4c4d6b |
* operator `[i,j,...]`. For instance, `$.store.book[0,2]` matches the
|
|
Packit |
4c4d6b |
* elements 0 and 2 (the first and third) of the book array of the store
|
|
Packit |
4c4d6b |
* object.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* * Slices of element nodes can be accessed using the slice notation
|
|
Packit |
4c4d6b |
* operation `[start:end:step]`. If start is omitted, the starting index
|
|
Packit |
4c4d6b |
* of the slice is implied to be zero; if end is omitted, the ending index
|
|
Packit |
4c4d6b |
* of the slice is implied to be the length of the array; if step is
|
|
Packit |
4c4d6b |
* omitted, the step of the slice is implied to be 1. For instance,
|
|
Packit |
4c4d6b |
* `$.store.book[:2]` matches the first two elements of the book array
|
|
Packit |
4c4d6b |
* of the store object.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* More information about JSONPath is available on Stefan Gössner's
|
|
Packit |
4c4d6b |
* [JSONPath website](http://goessner.net/articles/JsonPath/).
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* ## Example of JSONPath matches
|
|
Packit |
4c4d6b |
* The following example shows some of the results of using #JsonPath
|
|
Packit |
4c4d6b |
* on a JSON tree. We use the following JSON description of a bookstore:
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* |[
|
|
Packit |
4c4d6b |
* { "store": {
|
|
Packit |
4c4d6b |
* "book": [
|
|
Packit |
4c4d6b |
* { "category": "reference", "author": "Nigel Rees",
|
|
Packit |
4c4d6b |
* "title": "Sayings of the Century", "price": "8.95" },
|
|
Packit |
4c4d6b |
* { "category": "fiction", "author": "Evelyn Waugh",
|
|
Packit |
4c4d6b |
* "title": "Sword of Honour", "price": "12.99" },
|
|
Packit |
4c4d6b |
* { "category": "fiction", "author": "Herman Melville",
|
|
Packit |
4c4d6b |
* "title": "Moby Dick", "isbn": "0-553-21311-3",
|
|
Packit |
4c4d6b |
* "price": "8.99" },
|
|
Packit |
4c4d6b |
* { "category": "fiction", "author": "J. R. R. Tolkien",
|
|
Packit |
4c4d6b |
* "title": "The Lord of the Rings", "isbn": "0-395-19395-8",
|
|
Packit |
4c4d6b |
* "price": "22.99" }
|
|
Packit |
4c4d6b |
* ],
|
|
Packit |
4c4d6b |
* "bicycle": { "color": "red", "price": "19.95" }
|
|
Packit |
4c4d6b |
* }
|
|
Packit |
4c4d6b |
* }
|
|
Packit |
4c4d6b |
* ]|
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* We can parse the JSON using #JsonParser:
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* |[
|
|
Packit |
4c4d6b |
* JsonParser *parser = json_parser_new ();
|
|
Packit |
4c4d6b |
* json_parser_load_from_data (parser, json_data, -1, NULL);
|
|
Packit |
4c4d6b |
* ]|
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* If we run the following code:
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* |[
|
|
Packit |
4c4d6b |
* JsonNode *result;
|
|
Packit |
4c4d6b |
* JsonPath *path = json_path_new ();
|
|
Packit |
4c4d6b |
* json_path_compile (path, "$.store..author", NULL);
|
|
Packit |
4c4d6b |
* result = json_path_match (path, json_parser_get_root (parser));
|
|
Packit |
4c4d6b |
* ]|
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* The result #JsonNode will contain an array with all values of the
|
|
Packit |
4c4d6b |
* author member of the objects in the JSON tree. If we use a
|
|
Packit |
4c4d6b |
* #JsonGenerator to convert the #JsonNode to a string and print it:
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* |[
|
|
Packit |
4c4d6b |
* JsonGenerator *generator = json_generator_new ();
|
|
Packit |
4c4d6b |
* json_generator_set_root (generator, result);
|
|
Packit |
4c4d6b |
* char *str = json_generator_to_data (generator, NULL);
|
|
Packit |
4c4d6b |
* g_print ("Results: %s\n", str);
|
|
Packit |
4c4d6b |
* ]|
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* The output will be:
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* |[
|
|
Packit |
4c4d6b |
* ["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]
|
|
Packit |
4c4d6b |
* ]|
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* #JsonPath is available since JSON-GLib 0.14
|
|
Packit |
4c4d6b |
*/
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
#include "config.h"
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
#include <string.h>
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
#include <glib/gi18n-lib.h>
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
#include "json-path.h"
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
#include "json-debug.h"
|
|
Packit |
4c4d6b |
#include "json-types-private.h"
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
typedef enum {
|
|
Packit |
4c4d6b |
JSON_PATH_NODE_ROOT,
|
|
Packit |
4c4d6b |
JSON_PATH_NODE_CHILD_MEMBER,
|
|
Packit |
4c4d6b |
JSON_PATH_NODE_CHILD_ELEMENT,
|
|
Packit |
4c4d6b |
JSON_PATH_NODE_RECURSIVE_DESCENT,
|
|
Packit |
4c4d6b |
JSON_PATH_NODE_WILDCARD_MEMBER,
|
|
Packit |
4c4d6b |
JSON_PATH_NODE_WILDCARD_ELEMENT,
|
|
Packit |
4c4d6b |
JSON_PATH_NODE_ELEMENT_SET,
|
|
Packit |
4c4d6b |
JSON_PATH_NODE_ELEMENT_SLICE
|
|
Packit |
4c4d6b |
} PathNodeType;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
typedef struct _PathNode PathNode;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
struct _JsonPath
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
GObject parent_instance;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
/* the compiled path */
|
|
Packit |
4c4d6b |
GList *nodes;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
guint is_compiled : 1;
|
|
Packit |
4c4d6b |
};
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
struct _JsonPathClass
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
GObjectClass parent_class;
|
|
Packit |
4c4d6b |
};
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
struct _PathNode
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
PathNodeType node_type;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
union {
|
|
Packit |
4c4d6b |
/* JSON_PATH_NODE_CHILD_ELEMENT */
|
|
Packit |
4c4d6b |
int element_index;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
/* JSON_PATH_NODE_CHILD_MEMBER */
|
|
Packit |
4c4d6b |
char *member_name;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
/* JSON_PATH_NODE_ELEMENT_SET */
|
|
Packit |
4c4d6b |
struct { int n_indices; int *indices; } set;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
/* JSON_PATH_NODE_ELEMENT_SLICE */
|
|
Packit |
4c4d6b |
struct { int start, end, step; } slice;
|
|
Packit |
4c4d6b |
} data;
|
|
Packit |
4c4d6b |
};
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
G_DEFINE_QUARK (json-path-error-quark, json_path_error)
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
G_DEFINE_TYPE (JsonPath, json_path, G_TYPE_OBJECT)
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
static void
|
|
Packit |
4c4d6b |
path_node_free (gpointer data)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
if (data != NULL)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
PathNode *node = data;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
switch (node->node_type)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_CHILD_MEMBER:
|
|
Packit |
4c4d6b |
g_free (node->data.member_name);
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_ELEMENT_SET:
|
|
Packit |
4c4d6b |
g_free (node->data.set.indices);
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
default:
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_free (node);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
static void
|
|
Packit |
4c4d6b |
json_path_finalize (GObject *gobject)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonPath *self = JSON_PATH (gobject);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_list_free_full (self->nodes, path_node_free);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
G_OBJECT_CLASS (json_path_parent_class)->finalize (gobject);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
static void
|
|
Packit |
4c4d6b |
json_path_class_init (JsonPathClass *klass)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
G_OBJECT_CLASS (klass)->finalize = json_path_finalize;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
static void
|
|
Packit |
4c4d6b |
json_path_init (JsonPath *self)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
/**
|
|
Packit |
4c4d6b |
* json_path_new:
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Creates a new #JsonPath instance.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Once created, the #JsonPath object should be used with json_path_compile()
|
|
Packit |
4c4d6b |
* and json_path_match().
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Return value: (transfer full): the newly created #JsonPath instance. Use
|
|
Packit |
4c4d6b |
* g_object_unref() to free the allocated resources when done
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Since: 0.14
|
|
Packit |
4c4d6b |
*/
|
|
Packit |
4c4d6b |
JsonPath *
|
|
Packit |
4c4d6b |
json_path_new (void)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
return g_object_new (JSON_TYPE_PATH, NULL);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
#ifdef JSON_ENABLE_DEBUG
|
|
Packit |
4c4d6b |
/* used as the function for a g_list_foreach() on a list of PathNode; needs
|
|
Packit |
4c4d6b |
* a GString as the payload to build the output string
|
|
Packit |
4c4d6b |
*/
|
|
Packit |
4c4d6b |
static void
|
|
Packit |
4c4d6b |
json_path_foreach_print (gpointer data,
|
|
Packit |
4c4d6b |
gpointer user_data)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
PathNode *cur_node = data;
|
|
Packit |
4c4d6b |
GString *buf = user_data;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
switch (cur_node->node_type)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_ROOT:
|
|
Packit |
4c4d6b |
g_string_append (buf, "
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_CHILD_MEMBER:
|
|
Packit |
4c4d6b |
g_string_append_printf (buf, "<member '%s'", cur_node->data.member_name);
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_CHILD_ELEMENT:
|
|
Packit |
4c4d6b |
g_string_append_printf (buf, "<element '%d'", cur_node->data.element_index);
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_RECURSIVE_DESCENT:
|
|
Packit |
4c4d6b |
g_string_append (buf, "
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_WILDCARD_MEMBER:
|
|
Packit |
4c4d6b |
g_string_append (buf, "
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_WILDCARD_ELEMENT:
|
|
Packit |
4c4d6b |
g_string_append (buf, "
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_ELEMENT_SET:
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
int i;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_string_append (buf, "
|
|
Packit |
4c4d6b |
for (i = 0; i < cur_node->data.set.n_indices - 1; i++)
|
|
Packit |
4c4d6b |
g_string_append_printf (buf, "'%d', ", cur_node->data.set.indices[i]);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_string_append_printf (buf, "'%d'", cur_node->data.set.indices[i]);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_ELEMENT_SLICE:
|
|
Packit |
4c4d6b |
g_string_append_printf (buf, "
|
|
Packit |
4c4d6b |
cur_node->data.slice.start,
|
|
Packit |
4c4d6b |
cur_node->data.slice.end,
|
|
Packit |
4c4d6b |
cur_node->data.slice.step);
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
default:
|
|
Packit |
4c4d6b |
g_string_append (buf, "
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_string_append (buf, ">");
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
#endif /* JSON_ENABLE_DEBUG */
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
/**
|
|
Packit |
4c4d6b |
* json_path_compile:
|
|
Packit |
4c4d6b |
* @path: a #JsonPath
|
|
Packit |
4c4d6b |
* @expression: a JSONPath expression
|
|
Packit |
4c4d6b |
* @error: return location for a #GError, or %NULL
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Validates and decomposes @expression.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* A JSONPath expression must be compiled before calling json_path_match().
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Return value: %TRUE on success; on error, @error will be set with
|
|
Packit |
4c4d6b |
* the %JSON_PATH_ERROR domain and a code from the #JsonPathError
|
|
Packit |
4c4d6b |
* enumeration, and %FALSE will be returned
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Since: 0.14
|
|
Packit |
4c4d6b |
*/
|
|
Packit |
4c4d6b |
gboolean
|
|
Packit |
4c4d6b |
json_path_compile (JsonPath *path,
|
|
Packit |
4c4d6b |
const char *expression,
|
|
Packit |
4c4d6b |
GError **error)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
const char *p, *end_p;
|
|
Packit |
4c4d6b |
PathNode *root = NULL;
|
|
Packit |
4c4d6b |
GList *nodes = NULL;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_return_val_if_fail (expression != NULL, FALSE);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
p = expression;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
while (*p != '\0')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
switch (*p)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
case '$':
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
PathNode *node;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (root != NULL)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
g_set_error_literal (error, JSON_PATH_ERROR,
|
|
Packit |
4c4d6b |
JSON_PATH_ERROR_INVALID_QUERY,
|
|
Packit |
4c4d6b |
_("Only one root node is allowed in a JSONPath expression"));
|
|
Packit |
4c4d6b |
return FALSE;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (!(*(p + 1) == '.' || *(p + 1) == '[' || *(p + 1) == '\0'))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
g_set_error (error, JSON_PATH_ERROR,
|
|
Packit |
4c4d6b |
JSON_PATH_ERROR_INVALID_QUERY,
|
|
Packit |
4c4d6b |
/* translators: the %c is the invalid character */
|
|
Packit |
4c4d6b |
_("Root node followed by invalid character “%c”"),
|
|
Packit |
4c4d6b |
*(p + 1));
|
|
Packit |
4c4d6b |
return FALSE;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_ROOT;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
root = node;
|
|
Packit |
4c4d6b |
nodes = g_list_prepend (NULL, root);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case '.':
|
|
Packit |
4c4d6b |
case '[':
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
PathNode *node = NULL;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (*p == '.' && *(p + 1) == '.')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_RECURSIVE_DESCENT;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else if (*p == '.' && *(p + 1) == '*')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_WILDCARD_MEMBER;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
p += 1;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else if (*p == '.')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
end_p = p + 1;
|
|
Packit |
4c4d6b |
while (!(*end_p == '.' || *end_p == '[' || *end_p == '\0'))
|
|
Packit |
4c4d6b |
end_p += 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (end_p == p + 1)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
g_set_error_literal (error, JSON_PATH_ERROR,
|
|
Packit |
4c4d6b |
JSON_PATH_ERROR_INVALID_QUERY,
|
|
Packit |
4c4d6b |
_("Missing member name or wildcard after . character"));
|
|
Packit |
4c4d6b |
goto fail;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_CHILD_MEMBER;
|
|
Packit |
4c4d6b |
node->data.member_name = g_strndup (p + 1, end_p - p - 1);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
p = end_p - 1;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else if (*p == '[' && *(p + 1) == '\'')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
if (*(p + 2) == '*' && *(p + 3) == '\'' && *(p + 4) == ']')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_WILDCARD_MEMBER;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
p += 4;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_CHILD_MEMBER;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
end_p = strchr (p + 2, '\'');
|
|
Packit |
4c4d6b |
node->data.member_name = g_strndup (p + 2, end_p - p - 2);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
p = end_p + 1;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else if (*p == '[' && *(p + 1) == '*' && *(p + 2) == ']')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_WILDCARD_ELEMENT;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
p += 1;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else if (*p == '[')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
int sign = 1;
|
|
Packit |
4c4d6b |
int idx;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
end_p = p + 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (*end_p == '-')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
sign = -1;
|
|
Packit |
4c4d6b |
end_p += 1;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
/* slice with missing start */
|
|
Packit |
4c4d6b |
if (*end_p == ':')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
int slice_end = g_ascii_strtoll (end_p + 1, (char **) &end_p, 10) * sign;
|
|
Packit |
4c4d6b |
int slice_step = 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (*end_p == ':')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
end_p += 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (*end_p == '-')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
sign = -1;
|
|
Packit |
4c4d6b |
end_p += 1;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
sign = 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
slice_step = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (*end_p != ']')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
g_set_error (error, JSON_PATH_ERROR,
|
|
Packit |
4c4d6b |
JSON_PATH_ERROR_INVALID_QUERY,
|
|
Packit |
4c4d6b |
_("Malformed slice expression “%*s”"),
|
|
Packit |
4c4d6b |
(int)(end_p - p),
|
|
Packit |
4c4d6b |
p + 1);
|
|
Packit |
4c4d6b |
goto fail;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_ELEMENT_SLICE;
|
|
Packit |
4c4d6b |
node->data.slice.start = 0;
|
|
Packit |
4c4d6b |
node->data.slice.end = slice_end;
|
|
Packit |
4c4d6b |
node->data.slice.step = slice_step;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
nodes = g_list_prepend (nodes, node);
|
|
Packit |
4c4d6b |
p = end_p;
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
idx = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (*end_p == ',')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
GArray *indices = g_array_new (FALSE, TRUE, sizeof (int));
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_array_append_val (indices, idx);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
while (*end_p != ']')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
end_p += 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (*end_p == '-')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
sign = -1;
|
|
Packit |
4c4d6b |
end_p += 1;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
sign = 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
idx = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
|
|
Packit |
4c4d6b |
if (!(*end_p == ',' || *end_p == ']'))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
g_array_unref (indices);
|
|
Packit |
4c4d6b |
g_set_error (error, JSON_PATH_ERROR,
|
|
Packit |
4c4d6b |
JSON_PATH_ERROR_INVALID_QUERY,
|
|
Packit |
4c4d6b |
_("Invalid set definition “%*s”"),
|
|
Packit |
4c4d6b |
(int)(end_p - p),
|
|
Packit |
4c4d6b |
p + 1);
|
|
Packit |
4c4d6b |
goto fail;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_array_append_val (indices, idx);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_ELEMENT_SET;
|
|
Packit |
4c4d6b |
node->data.set.n_indices = indices->len;
|
|
Packit |
4c4d6b |
node->data.set.indices = (int *) g_array_free (indices, FALSE);
|
|
Packit |
4c4d6b |
nodes = g_list_prepend (nodes, node);
|
|
Packit |
4c4d6b |
p = end_p;
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else if (*end_p == ':')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
int slice_start = idx;
|
|
Packit |
4c4d6b |
int slice_end = 0;
|
|
Packit |
4c4d6b |
int slice_step = 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
end_p += 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (*end_p == '-')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
sign = -1;
|
|
Packit |
4c4d6b |
end_p += 1;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
sign = 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
slice_end = g_ascii_strtoll (end_p, (char **) &end_p, 10) * sign;
|
|
Packit |
4c4d6b |
if (*end_p == ':')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
end_p += 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (*end_p == '-')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
sign = -1;
|
|
Packit |
4c4d6b |
end_p += 1;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
sign = 1;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
slice_step = g_ascii_strtoll (end_p + 1, (char **) &end_p, 10) * sign;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (*end_p != ']')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
g_set_error (error, JSON_PATH_ERROR,
|
|
Packit |
4c4d6b |
JSON_PATH_ERROR_INVALID_QUERY,
|
|
Packit |
4c4d6b |
_("Invalid slice definition “%*s”"),
|
|
Packit |
4c4d6b |
(int)(end_p - p),
|
|
Packit |
4c4d6b |
p + 1);
|
|
Packit |
4c4d6b |
goto fail;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_ELEMENT_SLICE;
|
|
Packit |
4c4d6b |
node->data.slice.start = slice_start;
|
|
Packit |
4c4d6b |
node->data.slice.end = slice_end;
|
|
Packit |
4c4d6b |
node->data.slice.step = slice_step;
|
|
Packit |
4c4d6b |
nodes = g_list_prepend (nodes, node);
|
|
Packit |
4c4d6b |
p = end_p;
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else if (*end_p == ']')
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
node = g_new0 (PathNode, 1);
|
|
Packit |
4c4d6b |
node->node_type = JSON_PATH_NODE_CHILD_ELEMENT;
|
|
Packit |
4c4d6b |
node->data.element_index = idx;
|
|
Packit |
4c4d6b |
nodes = g_list_prepend (nodes, node);
|
|
Packit |
4c4d6b |
p = end_p;
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
g_set_error (error, JSON_PATH_ERROR,
|
|
Packit |
4c4d6b |
JSON_PATH_ERROR_INVALID_QUERY,
|
|
Packit |
4c4d6b |
_("Invalid array index definition “%*s”"),
|
|
Packit |
4c4d6b |
(int)(end_p - p),
|
|
Packit |
4c4d6b |
p + 1);
|
|
Packit |
4c4d6b |
goto fail;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (node != NULL)
|
|
Packit |
4c4d6b |
nodes = g_list_prepend (nodes, node);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
default:
|
|
Packit |
4c4d6b |
if (nodes == NULL)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
g_set_error(error, JSON_PATH_ERROR,
|
|
Packit |
4c4d6b |
JSON_PATH_ERROR_INVALID_QUERY,
|
|
Packit |
4c4d6b |
_("Invalid first character “%c”"),
|
|
Packit |
4c4d6b |
*p);
|
|
Packit |
4c4d6b |
return FALSE;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
p += 1;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
nodes = g_list_reverse (nodes);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
#ifdef JSON_ENABLE_DEBUG
|
|
Packit |
4c4d6b |
if (JSON_HAS_DEBUG (PATH))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
GString *buf = g_string_new (NULL);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_list_foreach (nodes, json_path_foreach_print, buf);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_message ("[PATH] " G_STRLOC ": expression '%s' => '%s'", expression, buf->str);
|
|
Packit |
4c4d6b |
g_string_free (buf, TRUE);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
#endif /* JSON_ENABLE_DEBUG */
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_list_free_full (path->nodes, path_node_free);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
path->nodes = nodes;
|
|
Packit |
4c4d6b |
path->is_compiled = (path->nodes != NULL);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
return path->nodes != NULL;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
fail:
|
|
Packit |
4c4d6b |
g_list_free_full (nodes, path_node_free);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
return FALSE;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
static void
|
|
Packit |
4c4d6b |
walk_path_node (GList *path,
|
|
Packit |
4c4d6b |
JsonNode *root,
|
|
Packit |
4c4d6b |
JsonArray *results)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
PathNode *node = path->data;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
switch (node->node_type)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_ROOT:
|
|
Packit |
4c4d6b |
if (path->next != NULL)
|
|
Packit |
4c4d6b |
walk_path_node (path->next, root, results);
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
json_array_add_element (results, json_node_copy (root));
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_CHILD_MEMBER:
|
|
Packit |
4c4d6b |
if (JSON_NODE_HOLDS_OBJECT (root))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonObject *object = json_node_get_object (root);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (json_object_has_member (object, node->data.member_name))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonNode *member = json_object_get_member (object, node->data.member_name);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (path->next == NULL)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JSON_NOTE (PATH, "end of path at member '%s'", node->data.member_name);
|
|
Packit |
4c4d6b |
json_array_add_element (results, json_node_copy (member));
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
walk_path_node (path->next, member, results);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_CHILD_ELEMENT:
|
|
Packit |
4c4d6b |
if (JSON_NODE_HOLDS_ARRAY (root))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonArray *array = json_node_get_array (root);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (json_array_get_length (array) >= node->data.element_index)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonNode *element = json_array_get_element (array, node->data.element_index);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (path->next == NULL)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JSON_NOTE (PATH, "end of path at element '%d'", node->data.element_index);
|
|
Packit |
4c4d6b |
json_array_add_element (results, json_node_copy (element));
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
walk_path_node (path->next, element, results);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_RECURSIVE_DESCENT:
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
PathNode *tmp = path->next->data;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
switch (json_node_get_node_type (root))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
case JSON_NODE_OBJECT:
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonObject *object = json_node_get_object (root);
|
|
Packit |
4c4d6b |
GQueue *members = json_object_get_members_internal (object);
|
|
Packit |
4c4d6b |
GList *l;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
for (l = members->head; l != NULL; l = l->next)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonNode *m = json_object_get_member (object, l->data);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (tmp->node_type == JSON_PATH_NODE_CHILD_MEMBER &&
|
|
Packit |
4c4d6b |
strcmp (tmp->data.member_name, l->data) == 0)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JSON_NOTE (PATH, "entering '%s'", tmp->data.member_name);
|
|
Packit |
4c4d6b |
walk_path_node (path->next, root, results);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JSON_NOTE (PATH, "recursing into '%s'", (char *) l->data);
|
|
Packit |
4c4d6b |
walk_path_node (path, m, results);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_NODE_ARRAY:
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonArray *array = json_node_get_array (root);
|
|
Packit |
4c4d6b |
GList *members, *l;
|
|
Packit |
4c4d6b |
int i;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
members = json_array_get_elements (array);
|
|
Packit |
4c4d6b |
for (l = members, i = 0; l != NULL; l = l->next, i += 1)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonNode *m = l->data;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (tmp->node_type == JSON_PATH_NODE_CHILD_ELEMENT &&
|
|
Packit |
4c4d6b |
tmp->data.element_index == i)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JSON_NOTE (PATH, "entering '%d'", tmp->data.element_index);
|
|
Packit |
4c4d6b |
walk_path_node (path->next, root, results);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JSON_NOTE (PATH, "recursing into '%d'", i);
|
|
Packit |
4c4d6b |
walk_path_node (path, m, results);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
g_list_free (members);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
default:
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_WILDCARD_MEMBER:
|
|
Packit |
4c4d6b |
if (JSON_NODE_HOLDS_OBJECT (root))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonObject *object = json_node_get_object (root);
|
|
Packit |
4c4d6b |
GQueue *members = json_object_get_members_internal (object);
|
|
Packit |
4c4d6b |
GList *l;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
for (l = members->head; l != NULL; l = l->next)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonNode *member = json_object_get_member (object, l->data);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (path->next != NULL)
|
|
Packit |
4c4d6b |
walk_path_node (path->next, member, results);
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JSON_NOTE (PATH, "glob match member '%s'", (char *) l->data);
|
|
Packit |
4c4d6b |
json_array_add_element (results, json_node_copy (member));
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
json_array_add_element (results, json_node_copy (root));
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_WILDCARD_ELEMENT:
|
|
Packit |
4c4d6b |
if (JSON_NODE_HOLDS_ARRAY (root))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonArray *array = json_node_get_array (root);
|
|
Packit |
4c4d6b |
GList *elements, *l;
|
|
Packit |
4c4d6b |
int i;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
elements = json_array_get_elements (array);
|
|
Packit |
4c4d6b |
for (l = elements, i = 0; l != NULL; l = l->next, i += 1)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonNode *element = l->data;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (path->next != NULL)
|
|
Packit |
4c4d6b |
walk_path_node (path->next, element, results);
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JSON_NOTE (PATH, "glob match element '%d'", i);
|
|
Packit |
4c4d6b |
json_array_add_element (results, json_node_copy (element));
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
g_list_free (elements);
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
json_array_add_element (results, json_node_copy (root));
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_ELEMENT_SET:
|
|
Packit |
4c4d6b |
if (JSON_NODE_HOLDS_ARRAY (root))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonArray *array = json_node_get_array (root);
|
|
Packit |
4c4d6b |
int i;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
for (i = 0; i < node->data.set.n_indices; i += 1)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
int idx = node->data.set.indices[i];
|
|
Packit |
4c4d6b |
JsonNode *element = json_array_get_element (array, idx);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (path->next != NULL)
|
|
Packit |
4c4d6b |
walk_path_node (path->next, element, results);
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JSON_NOTE (PATH, "set element '%d'", idx);
|
|
Packit |
4c4d6b |
json_array_add_element (results, json_node_copy (element));
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
case JSON_PATH_NODE_ELEMENT_SLICE:
|
|
Packit |
4c4d6b |
if (JSON_NODE_HOLDS_ARRAY (root))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonArray *array = json_node_get_array (root);
|
|
Packit |
4c4d6b |
int i, start, end;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (node->data.slice.start < 0)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
start = json_array_get_length (array)
|
|
Packit |
4c4d6b |
+ node->data.slice.start;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
end = json_array_get_length (array)
|
|
Packit |
4c4d6b |
+ node->data.slice.end;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
start = node->data.slice.start;
|
|
Packit |
4c4d6b |
end = node->data.slice.end;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
for (i = start; i < end; i += node->data.slice.step)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonNode *element = json_array_get_element (array, i);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (path->next != NULL)
|
|
Packit |
4c4d6b |
walk_path_node (path->next, element, results);
|
|
Packit |
4c4d6b |
else
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JSON_NOTE (PATH, "slice element '%d'", i);
|
|
Packit |
4c4d6b |
json_array_add_element (results, json_node_copy (element));
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
default:
|
|
Packit |
4c4d6b |
break;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
/**
|
|
Packit |
4c4d6b |
* json_path_match:
|
|
Packit |
4c4d6b |
* @path: a compiled #JsonPath
|
|
Packit |
4c4d6b |
* @root: a #JsonNode
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Matches the JSON tree pointed by @root using the expression compiled
|
|
Packit |
4c4d6b |
* into the #JsonPath.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* The matching #JsonNodes will be copied into a #JsonArray and
|
|
Packit |
4c4d6b |
* returned wrapped in a #JsonNode.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Return value: (transfer full): a newly-created #JsonNode of type
|
|
Packit |
4c4d6b |
* %JSON_NODE_ARRAY containing an array of matching #JsonNodes.
|
|
Packit |
4c4d6b |
* Use json_node_unref() when done
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Since: 0.14
|
|
Packit |
4c4d6b |
*/
|
|
Packit |
4c4d6b |
JsonNode *
|
|
Packit |
4c4d6b |
json_path_match (JsonPath *path,
|
|
Packit |
4c4d6b |
JsonNode *root)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonArray *results;
|
|
Packit |
4c4d6b |
JsonNode *retval;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_return_val_if_fail (JSON_IS_PATH (path), NULL);
|
|
Packit |
4c4d6b |
g_return_val_if_fail (path->is_compiled, NULL);
|
|
Packit |
4c4d6b |
g_return_val_if_fail (root != NULL, NULL);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
results = json_array_new ();
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
walk_path_node (path->nodes, root, results);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
retval = json_node_new (JSON_NODE_ARRAY);
|
|
Packit |
4c4d6b |
json_node_take_array (retval, results);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
return retval;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
/**
|
|
Packit |
4c4d6b |
* json_path_query:
|
|
Packit |
4c4d6b |
* @expression: a JSONPath expression
|
|
Packit |
4c4d6b |
* @root: the root of a JSON tree
|
|
Packit |
4c4d6b |
* @error: return location for a #GError, or %NULL
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Queries a JSON tree using a JSONPath expression.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* This function is a simple wrapper around json_path_new(),
|
|
Packit |
4c4d6b |
* json_path_compile() and json_path_match(). It implicitly
|
|
Packit |
4c4d6b |
* creates a #JsonPath instance, compiles @expression and
|
|
Packit |
4c4d6b |
* matches it against the JSON tree pointed by @root.
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Return value: (transfer full): a newly-created #JsonNode of type
|
|
Packit |
4c4d6b |
* %JSON_NODE_ARRAY containing an array of matching #JsonNodes.
|
|
Packit |
4c4d6b |
* Use json_node_unref() when done
|
|
Packit |
4c4d6b |
*
|
|
Packit |
4c4d6b |
* Since: 0.14
|
|
Packit |
4c4d6b |
*/
|
|
Packit |
4c4d6b |
JsonNode *
|
|
Packit |
4c4d6b |
json_path_query (const char *expression,
|
|
Packit |
4c4d6b |
JsonNode *root,
|
|
Packit |
4c4d6b |
GError **error)
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
JsonPath *path = json_path_new ();
|
|
Packit |
4c4d6b |
JsonNode *retval;
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
if (!json_path_compile (path, expression, error))
|
|
Packit |
4c4d6b |
{
|
|
Packit |
4c4d6b |
g_object_unref (path);
|
|
Packit |
4c4d6b |
return NULL;
|
|
Packit |
4c4d6b |
}
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
retval = json_path_match (path, root);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
g_object_unref (path);
|
|
Packit |
4c4d6b |
|
|
Packit |
4c4d6b |
return retval;
|
|
Packit |
4c4d6b |
}
|