diff options
author | Emmanuele Bassi <ebassi@linux.intel.com> | 2010-08-12 15:29:41 +0100 |
---|---|---|
committer | Emmanuele Bassi <ebassi@linux.intel.com> | 2010-08-12 15:35:47 +0100 |
commit | 10e5a1d38113b7b6e7c71da76ff11219baf1022d (patch) | |
tree | d21219ef532acebdc45107369e3f74abfb53cbc7 /json-glib/json-reader.c | |
parent | c3215ba1d46f7965fb58272da069bec389a174df (diff) | |
download | json-glib-10e5a1d38113b7b6e7c71da76ff11219baf1022d.tar.gz |
Add JsonReader
JsonReader is a simple, cursor-based API for parsing a JSON DOM. It is
similar, in spirit, to the XmlReader API provided by various platforms
and XML parsing libraries.
Diffstat (limited to 'json-glib/json-reader.c')
-rw-r--r-- | json-glib/json-reader.c | 779 |
1 files changed, 779 insertions, 0 deletions
diff --git a/json-glib/json-reader.c b/json-glib/json-reader.c new file mode 100644 index 0000000..adc859e --- /dev/null +++ b/json-glib/json-reader.c @@ -0,0 +1,779 @@ +/* json-reader.h - JSON cursor parser + * + * This file is part of JSON-GLib + * Copyright (C) 2010 Intel Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Author: + * Emmanuele Bassi <ebassi@linux.intel.com> + */ + +/** + * SECTION:json-reader + * @Title: JsonReader + * @short_description: A cursor-based parser + * + * #JsonReader provides a simple, cursor-based API for parsing a JSON DOM. It + * is similar, in spirit, to the XML Reader API. + * + * In case of error, #JsonReader will be set in an error state; all subsequent + * calls will simply be ignored until a function that resets the error state is + * called, e.g.: + * + * |[ + * /* ask for the 7th element; if the element does not exist, the + * * reader will be put in an error state + * */ + * json_reader_read_element (reader, 6); + * + * /* in case of error, this will return NULL, otherwise it will + * * return the value of the element + * */ + * str = json_reader_get_value_string (value); + * + * /* this function resets the error state if any was set */ + * json_reader_end_element (reader); + * ]| + * + * If you want to detect the error state as soon as possible, you can use + * json_reader_get_error(): + * + * |[ + * /* like the example above, but in this case we print out the + * * error immediately + * */ + * if (!json_reader_read_element (reader, 6)) + * { + * const GError *error = json_reader_get_error (error); + * g_print ("Unable to read the element: %s", error->message); + * } + * ]| + * + * #JsonReader is available since JSON-GLib 0.12. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "json-reader.h" + +#include "json-types-private.h" + +#include "json-debug.h" +#include "json-parser.h" + +#define json_reader_return_if_error_set(r) G_STMT_START { \ + if (((JsonReader *) (r))->priv->error != NULL) \ + return; } G_STMT_END + +#define json_reader_return_val_if_error_set(r,v) G_STMT_START { \ + if (((JsonReader *) (r))->priv->error != NULL) \ + return (v); } G_STMT_END + +struct _JsonReaderPrivate +{ + JsonParser *parser; + + JsonNode *root; + + JsonNode *current_node; + JsonNode *previous_node; + + GError *error; +}; + +G_DEFINE_TYPE (JsonReader, json_reader, G_TYPE_OBJECT); + +static void +json_reader_dispose (GObject *gobject) +{ + JsonReaderPrivate *priv = JSON_READER (gobject)->priv; + + if (priv->parser != NULL) + { + g_object_unref (priv->parser); + + priv->parser = NULL; + priv->root = NULL; + priv->current_node = NULL; + priv->previous_node = NULL; + } + + if (priv->error != NULL) + g_clear_error (&priv->error); + + G_OBJECT_CLASS (json_reader_parent_class)->dispose (gobject); +} + +static void +json_reader_class_init (JsonReaderClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (JsonReaderPrivate)); + + gobject_class->dispose = json_reader_dispose; +} + +static void +json_reader_init (JsonReader *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, JSON_TYPE_READER, + JsonReaderPrivate); + + self->priv->parser = json_parser_new (); +} + +GQuark +json_reader_error_quark (void) +{ + return g_quark_from_static_string ("json-reader-error"); +} + +/** + * json_reader_new: + * + * Creates a new #JsonReader instance + * + * Return value: the newly created #JsonReader. Use g_object_unref() to + * release the allocated resources when done + * + * Since: 0.12 + */ +JsonReader * +json_reader_new (void) +{ + return g_object_new (JSON_TYPE_READER, NULL); +} + +/** + * json_reader_load_from_data: + * @reader: a #JsonReader + * @data: the data to be parsed + * @length: the length of @data, or -1 + * @error: return location for a #GError, or %NULL + * + * Loads a JSON string and parses it. + * + * If @reader already contained a JSON DOM, it will be reset. + * + * Return value: %TRUE if the data was successfully parsed, and %FALSE + * otherwise. In case of failure, the #GError will be set accordingly + * + * Since: 0.12 + */ +gboolean +json_reader_load_from_data (JsonReader *reader, + const gchar *data, + gssize length, + GError **error) +{ + JsonReaderPrivate *priv; + GError *internal_error; + gboolean retval; + + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + + priv = reader->priv; + + if (priv->root != NULL) + { + priv->root = NULL; + + priv->current_node = NULL; + priv->previous_node = NULL; + } + + if (priv->error != NULL) + g_clear_error (&priv->error); + + internal_error = NULL; + retval = json_parser_load_from_data (priv->parser, data, length, &internal_error); + if (retval) + { + priv->root = json_parser_get_root (priv->parser); + + priv->current_node = priv->root; + priv->previous_node = NULL; + + priv->error = NULL; + } + else + { + priv->error = g_error_copy (internal_error); + g_propagate_error (error, internal_error); + } + + return retval; +} + +/* + * json_reader_unset_error: + * @reader: a #JsonReader + * + * Unsets the error state of @reader, if set + */ +static inline void +json_reader_unset_error (JsonReader *reader) +{ + if (reader->priv->error != NULL) + g_clear_error (&(reader->priv->error)); +} + +/* + * json_reader_ser_error: + * @reader: a #JsonReader + * @error_code: the #JsonReaderError code for the error + * @error_fmt: format string + * @Varargs: list of arguments for the @error_fmt string + * + * Sets the error state of @reader using the given error code + * and string + * + * Return value: %FALSE, to be used to return immediately from + * the caller function + */ +static gboolean +json_reader_set_error (JsonReader *reader, + JsonReaderError error_code, + const gchar *error_fmt, + ...) +{ + JsonReaderPrivate *priv = reader->priv; + va_list args; + gchar *error_msg; + + if (priv->error != NULL) + g_clear_error (&priv->error); + + va_start (args, error_fmt); + error_msg = g_strdup_vprintf (error_fmt, args); + va_end (args); + + g_set_error_literal (&priv->error, JSON_READER_ERROR, + error_code, + error_msg); + + g_free (error_msg); + + return FALSE; +} + +/** + * json_reader_get_error: + * @reader: a #JsonReader + * + * Retrieves the #GError currently set on @reader, if the #JsonReader + * is in error state + * + * Return value: (transfer none): the pointer to the error, or %NULL + * + * Since: 0.12 + */ +G_CONST_RETURN GError * +json_reader_get_error (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + + return reader->priv->error; +} + +/** + * json_reader_is_array: + * @reader: a #JsonReader + * + * Checks whether the @reader is currently on an array + * + * Return value: %TRUE if the #JsonReader is on an array, and %FALSE + * otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_is_array (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + if (reader->priv->current_node == NULL) + return FALSE; + + return JSON_NODE_HOLDS_ARRAY (reader->priv->current_node); +} + +/** + * json_reader_is_object: + * @reader: a #JsonReader + * + * Checks whether the @reader is currently on an object + * + * Return value: %TRUE if the #JsonReader is on an object, and %FALSE + * otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_is_object (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + if (reader->priv->current_node == NULL) + return FALSE; + + return JSON_NODE_HOLDS_OBJECT (reader->priv->current_node); +} + +/** + * json_reader_is_value: + * @reader: a #JsonReader + * + * Checks whether the @reader is currently on a value + * + * Return value: %TRUE if the #JsonReader is on a value, and %FALSE + * otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_is_value (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + if (reader->priv->current_node == NULL) + return FALSE; + + return JSON_NODE_HOLDS_VALUE (reader->priv->current_node); +} + +/** + * json_reader_read_element: + * @reader: a #JsonReader + * @index_: the index of the element + * + * Advances the cursor of @reader to the element @index_ of array at the + * current position. + * + * You can use the json_reader_get_value* family of functions to retrieve + * the value of the element; for instance: + * + * |[ + * json_reader_read_element (reader, 0); + * int_value = json_reader_get_value_int (reader); + * ]| + * + * After reading the value, json_reader_end_element() should be called to + * reposition the cursor inside the #JsonReader, e.g.: + * + * |[ + * json_reader_read_element (reader, 1); + * str_value = json_reader_get_value_string (reader); + * json_reader_end_element (reader); + * + * json_reader_read_element (reader, 2); + * str_value = json_reader_get_value_string (reader); + * json_reader_end_element (reader); + * ]| + * + * If @reader is not currently on an array, or if the @index_ is bigger than + * the size of the array, the #JsonReader will be put in an error state until + * json_reader_end_element() is called. + * + * Return value: %TRUE on success, and %FALSE otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_read_element (JsonReader *reader, + guint index_) +{ + JsonReaderPrivate *priv; + JsonArray *array; + + g_return_val_if_fail (JSON_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + priv = reader->priv; + + if (priv->current_node == NULL) + priv->current_node = priv->root; + + if (!JSON_NODE_HOLDS_ARRAY (priv->current_node)) + return json_reader_set_error (reader, JSON_READER_ERROR_NO_ARRAY, + "The current node is of type '%s', but " + "an array was expected.", + json_node_type_name (priv->current_node)); + + array = json_node_get_array (priv->current_node); + if (index_ >= json_array_get_length (array)) + return json_reader_set_error (reader, JSON_READER_ERROR_INVALID_INDEX, + "The index '%d' is greater than the size " + "of the array at the current position.", + index_); + + priv->previous_node = priv->current_node; + priv->current_node = json_array_get_element (array, index_); + + return TRUE; +} + +/** + * json_reader_end_element: + * @reader: a #JsonReader + * + * Moves the cursor back to the previous node after being positioned + * inside an array + * + * This function resets the error state of @reader, if any was set + * + * Since: 0.12 + */ +void +json_reader_end_element (JsonReader *reader) +{ + JsonReaderPrivate *priv; + JsonNode *tmp; + + g_return_if_fail (JSON_IS_READER (reader)); + + json_reader_unset_error (reader); + + priv = reader->priv; + + if (priv->previous_node != NULL) + tmp = json_node_get_parent (priv->previous_node); + else + tmp = NULL; + + priv->current_node = priv->previous_node; + priv->previous_node = tmp; +} + +/** + * json_reader_count_elements: + * @reader: a #JsonReader + * + * Counts the elements of the current position, if @reader is + * positioned on an array + * + * Return value: the number of elements, or -1. In case of failure + * the #JsonReader is set in an error state + * + * Since: 0.12 + */ +gint +json_reader_count_elements (JsonReader *reader) +{ + JsonReaderPrivate *priv; + + g_return_val_if_fail (JSON_IS_READER (reader), -1); + + priv = reader->priv; + + if (priv->current_node == NULL) + return -1; + + if (!JSON_NODE_HOLDS_ARRAY (priv->current_node)) + return -1; + + return json_array_get_length (json_node_get_array (priv->current_node)); +} + +/** + * json_reader_read_member: + * @reader: a #JsonReader + * @member_name: the name of the member to read + * + * Advances the cursor of @reader to the @member_name of the object at the + * current position. + * + * You can use the json_reader_get_value* family of functions to retrieve + * the value of the member; for instance: + * + * |[ + * json_reader_read_member (reader, "width"); + * width = json_reader_get_value_int (reader); + * ]| + * + * After reading the value, json_reader_end_member() should be called to + * reposition the cursor inside the #JsonReader, e.g.: + * + * |[ + * json_reader_read_member (reader, "author"); + * author = json_reader_get_value_string (reader); + * json_reader_end_element (reader); + * + * json_reader_read_element (reader, "title"); + * title = json_reader_get_value_string (reader); + * json_reader_end_element (reader); + * ]| + * + * If @reader is not currently on an object, or if the @member_name is not + * defined in the object, the #JsonReader will be put in an error state until + * json_reader_end_member() is called. + * + * Return value: %TRUE on success, and %FALSE otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_read_member (JsonReader *reader, + const gchar *member_name) +{ + JsonReaderPrivate *priv; + JsonObject *object; + + g_return_val_if_fail (JSON_READER (reader), FALSE); + g_return_val_if_fail (member_name != NULL, FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + priv = reader->priv; + + if (priv->current_node == NULL) + priv->current_node = priv->root; + + if (!JSON_NODE_HOLDS_OBJECT (priv->current_node)) + return json_reader_set_error (reader, JSON_READER_ERROR_NO_OBJECT, + "The current node is of type '%s', but " + "an object was expected.", + json_node_type_name (priv->current_node)); + + object = json_node_get_object (priv->current_node); + if (!json_object_has_member (object, member_name)) + return json_reader_set_error (reader, JSON_READER_ERROR_INVALID_MEMBER, + "The member '%s' is not defined in the " + "object at the current position.", + member_name); + + priv->previous_node = priv->current_node; + priv->current_node = json_object_get_member (object, member_name); + + return TRUE; +} + +/** + * json_reader_end_member: + * @reader: a #JsonReader + * + * Moves the cursor back to the previous node after being positioned + * inside an object + * + * This function resets the error state of @reader, if any was set + * + * Since: 0.12 + */ +void +json_reader_end_member (JsonReader *reader) +{ + JsonReaderPrivate *priv; + JsonNode *tmp; + + g_return_if_fail (JSON_IS_READER (reader)); + + json_reader_unset_error (reader); + + priv = reader->priv; + + if (priv->previous_node != NULL) + tmp = json_node_get_parent (priv->previous_node); + else + tmp = NULL; + + priv->current_node = priv->previous_node; + priv->previous_node = tmp; +} + +/** + * json_reader_count_members: + * @reader: a #JsonReader + * + * Counts the members of the current position, if @reader is + * positioned on an object + * + * Return value: the number of members, or -1. In case of failure + * the #JsonReader is set in an error state + * + * Since: 0.12 + */ +gint +json_reader_count_members (JsonReader *reader) +{ + JsonReaderPrivate *priv; + + g_return_val_if_fail (JSON_IS_READER (reader), -1); + + priv = reader->priv; + + if (priv->current_node == NULL) + return -1; + + if (!JSON_NODE_HOLDS_OBJECT (priv->current_node)) + return -1; + + return json_object_get_size (json_node_get_object (priv->current_node)); +} + +/** + * json_reader_get_value: + * @reader: a #JsonReader + * + * Retrieves the #JsonNode of the current position of @reader + * + * Return value: (transfer none): a #JsonNode, or %NULL. The returned node + * is owned by the #JsonReader and it should not be modified or freed + * directly + * + * Since: 0.12 + */ +JsonNode * +json_reader_get_value (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), NULL); + json_reader_return_val_if_error_set (reader, NULL); + + if (reader->priv->current_node == NULL) + return NULL; + + if (!JSON_NODE_HOLDS_VALUE (reader->priv->current_node)) + return NULL; + + return reader->priv->current_node; +} + +/** + * json_reader_get_value_int: + * @reader: a #JsonReader + * + * Retrieves the integer value of the current position of @reader + * + * Return value: the integer value + * + * Since: 0.12 + */ +gint64 +json_reader_get_value_int (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), 0); + json_reader_return_val_if_error_set (reader, 0); + + if (reader->priv->current_node == NULL) + return 0; + + if (!JSON_NODE_HOLDS_VALUE (reader->priv->current_node)) + return 0; + + return json_node_get_int (reader->priv->current_node); +} + +/** + * json_reader_get_value_double: + * @reader: a #JsonReader + * + * Retrieves the floating point value of the current position of @reader + * + * Return value: the floating point value + * + * Since: 0.12 + */ +gdouble +json_reader_get_value_double (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), 0.0); + json_reader_return_val_if_error_set (reader, 0.0); + + if (reader->priv->current_node == NULL) + return 0.0; + + if (!JSON_NODE_HOLDS_VALUE (reader->priv->current_node)) + return 0.0; + + return json_node_get_double (reader->priv->current_node); +} + +/** + * json_reader_get_value_string: + * @reader: a #JsonReader + * + * Retrieves the string value of the current position of @reader + * + * Return value: the string value + * + * Since: 0.12 + */ +G_CONST_RETURN gchar * +json_reader_get_value_string (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), NULL); + json_reader_return_val_if_error_set (reader, NULL); + + if (reader->priv->current_node == NULL) + return NULL; + + if (!JSON_NODE_HOLDS_VALUE (reader->priv->current_node)) + return NULL; + + return json_node_get_string (reader->priv->current_node); +} + +/** + * json_reader_get_value_boolean: + * @reader: a #JsonReader + * + * Retrieves the boolean value of the current position of @reader + * + * Return value: the boolean value + * + * Since: 0.12 + */ +gboolean +json_reader_get_value_boolean (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + if (reader->priv->current_node == NULL) + return FALSE; + + if (!JSON_NODE_HOLDS_VALUE (reader->priv->current_node)) + return FALSE; + + return json_node_get_boolean (reader->priv->current_node); +} + +/** + * json_reader_get_value_null: + * @reader: a #JsonReader + * + * Checks whether the value of the current position of @reader is 'null' + * + * Return value: %TRUE if 'null' is set, and %FALSE otherwise + * + * Since: 0.12 + */ +gboolean +json_reader_get_value_null (JsonReader *reader) +{ + g_return_val_if_fail (JSON_IS_READER (reader), FALSE); + json_reader_return_val_if_error_set (reader, FALSE); + + if (reader->priv->current_node == NULL) + return FALSE; + + return JSON_NODE_HOLDS_NULL (reader->priv->current_node); +} |