/* json-node.c - JSON object model node * * This file is part of JSON-GLib * Copyright (C) 2007 OpenedHand Ltd. * Copyright (C) 2009 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 . * * Author: * Emmanuele Bassi */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "json-types-private.h" /** * SECTION:json-node * @short_description: Node in a JSON object model * * A #JsonNode is a generic container of elements inside a JSON stream. * It can contain fundamental types (integers, booleans, floating point * numbers, strings) and complex types (arrays and objects). * * When parsing a JSON data stream you extract the root node and walk * the node tree by retrieving the type of data contained inside the * node with the %JSON_NODE_TYPE macro. If the node contains a fundamental * type you can retrieve a copy of the #GValue holding it with the * json_node_get_value() function, and then use the #GValue API to extract * the data; if the node contains a complex type you can retrieve the * #JsonObject or the #JsonArray using json_node_get_object() or * json_node_get_array() respectively, and then retrieve the nodes * they contain. */ G_DEFINE_BOXED_TYPE (JsonNode, json_node, json_node_copy, json_node_free); /** * json_node_get_value_type: * @node: a #JsonNode * * Returns the #GType of the payload of the node. * * Return value: a #GType for the payload. * * Since: 0.4 */ GType json_node_get_value_type (JsonNode *node) { g_return_val_if_fail (node != NULL, G_TYPE_INVALID); switch (node->type) { case JSON_NODE_OBJECT: return JSON_TYPE_OBJECT; case JSON_NODE_ARRAY: return JSON_TYPE_ARRAY; case JSON_NODE_NULL: return G_TYPE_INVALID; case JSON_NODE_VALUE: if (node->data.value) return JSON_VALUE_TYPE (node->data.value); else return G_TYPE_INVALID; default: g_assert_not_reached (); return G_TYPE_INVALID; } } /** * json_node_new: * @type: a #JsonNodeType * * Creates a new #JsonNode of @type. * * Return value: the newly created #JsonNode */ JsonNode * json_node_new (JsonNodeType type) { JsonNode *data; g_return_val_if_fail (type >= JSON_NODE_OBJECT && type <= JSON_NODE_NULL, NULL); data = g_slice_new0 (JsonNode); data->type = type; return data; } /** * json_node_copy: * @node: a #JsonNode * * Copies @node. If the node contains complex data types then the reference * count of the objects is increased. * * Return value: (transfer full): the copied #JsonNode */ JsonNode * json_node_copy (JsonNode *node) { JsonNode *copy; g_return_val_if_fail (node != NULL, NULL); copy = g_slice_new0 (JsonNode); copy->type = node->type; switch (copy->type) { case JSON_NODE_OBJECT: if (node->data.object) copy->data.object = json_object_ref (node->data.object); break; case JSON_NODE_ARRAY: if (node->data.array) copy->data.array = json_array_ref (node->data.array); break; case JSON_NODE_VALUE: if (node->data.value) copy->data.value = json_value_ref (node->data.value); break; case JSON_NODE_NULL: break; default: g_assert_not_reached (); } return copy; } /** * json_node_set_object: * @node: a #JsonNode * @object: a #JsonObject * * Sets @objects inside @node. The reference count of @object is increased. */ void json_node_set_object (JsonNode *node, JsonObject *object) { g_return_if_fail (node != NULL); g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT); if (node->data.object) json_object_unref (node->data.object); if (object) node->data.object = json_object_ref (object); else node->data.object = NULL; } /** * json_node_take_object: * @node: a #JsonNode * @object: (transfer full): a #JsonObject * * Sets @object inside @node. The reference count of @object is not increased. */ void json_node_take_object (JsonNode *node, JsonObject *object) { g_return_if_fail (node != NULL); g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT); if (node->data.object) { json_object_unref (node->data.object); node->data.object = NULL; } if (object) node->data.object = object; } /** * json_node_get_object: * @node: a #JsonNode * * Retrieves the #JsonObject stored inside a #JsonNode * * Return value: (transfer none): the #JsonObject */ JsonObject * json_node_get_object (JsonNode *node) { g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT, NULL); return node->data.object; } /** * json_node_dup_object: * @node: a #JsonNode * * Retrieves the #JsonObject inside @node. The reference count of * the returned object is increased. * * Return value: (transfer full): the #JsonObject */ JsonObject * json_node_dup_object (JsonNode *node) { g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_OBJECT, NULL); if (node->data.object) return json_object_ref (node->data.object); return NULL; } /** * json_node_set_array: * @node: a #JsonNode * @array: a #JsonArray * * Sets @array inside @node and increases the #JsonArray reference count */ void json_node_set_array (JsonNode *node, JsonArray *array) { g_return_if_fail (node != NULL); g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY); if (node->data.array) json_array_unref (node->data.array); if (array) node->data.array = json_array_ref (array); else node->data.array = NULL; } /** * json_node_take_array: * @node: a #JsonNode * @array: (transfer full): a #JsonArray * * Sets @array into @node without increasing the #JsonArray reference count. */ void json_node_take_array (JsonNode *node, JsonArray *array) { g_return_if_fail (node != NULL); g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY); if (node->data.array) { json_array_unref (node->data.array); node->data.array = NULL; } if (array) node->data.array = array; } /** * json_node_get_array: * @node: a #JsonNode * * Retrieves the #JsonArray stored inside a #JsonNode * * Return value: (transfer none): the #JsonArray */ JsonArray * json_node_get_array (JsonNode *node) { g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY, NULL); return node->data.array; } /** * json_node_dup_array: * @node: a #JsonNode * * Retrieves the #JsonArray stored inside a #JsonNode and returns it * with its reference count increased by one. * * Return value: (transfer full): the #JsonArray with its reference * count increased. */ JsonArray * json_node_dup_array (JsonNode *node) { g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_ARRAY, NULL); if (node->data.array) return json_array_ref (node->data.array); return NULL; } /** * json_node_get_value: * @node: a #JsonNode * @value: (out caller-allocates): return location for an uninitialized value * * Retrieves a value from a #JsonNode and copies into @value. When done * using it, call g_value_unset() on the #GValue. */ void json_node_get_value (JsonNode *node, GValue *value) { g_return_if_fail (node != NULL); g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); if (node->data.value) { g_value_init (value, JSON_VALUE_TYPE (node->data.value)); switch (JSON_VALUE_TYPE (node->data.value)) { case G_TYPE_INT64: g_value_set_int64 (value, json_value_get_int (node->data.value)); break; case G_TYPE_DOUBLE: g_value_set_double (value, json_value_get_double (node->data.value)); break; case G_TYPE_BOOLEAN: g_value_set_boolean (value, json_value_get_boolean (node->data.value)); break; case G_TYPE_STRING: g_value_set_string (value, json_value_get_string (node->data.value)); break; default: break; } } } /** * json_node_set_value: * @node: a #JsonNode * @value: the #GValue to set * * Sets @value inside @node. The passed #GValue is copied into the #JsonNode */ void json_node_set_value (JsonNode *node, const GValue *value) { g_return_if_fail (node != NULL); g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); g_return_if_fail (G_VALUE_TYPE (value) != G_TYPE_INVALID); if (node->data.value == NULL) node->data.value = json_value_alloc (); switch (G_VALUE_TYPE (value)) { /* auto-promote machine integers to 64 bit integers */ case G_TYPE_INT64: case G_TYPE_INT: json_value_init (node->data.value, JSON_VALUE_INT); if (G_VALUE_TYPE (value) == G_TYPE_INT64) json_value_set_int (node->data.value, g_value_get_int64 (value)); else json_value_set_int (node->data.value, g_value_get_int (value)); break; case G_TYPE_BOOLEAN: json_value_init (node->data.value, JSON_VALUE_BOOLEAN); json_value_set_boolean (node->data.value, g_value_get_boolean (value)); break; /* auto-promote single-precision floats to double precision floats */ case G_TYPE_DOUBLE: case G_TYPE_FLOAT: json_value_init (node->data.value, JSON_VALUE_DOUBLE); if (G_VALUE_TYPE (value) == G_TYPE_DOUBLE) json_value_set_double (node->data.value, g_value_get_double (value)); else json_value_set_double (node->data.value, g_value_get_float (value)); break; case G_TYPE_STRING: json_value_init (node->data.value, JSON_VALUE_STRING); json_value_set_string (node->data.value, g_value_get_string (value)); break; default: g_warning ("Invalid value of type '%s'", g_type_name (G_VALUE_TYPE (value))); return; } } /** * json_node_free: * @node: a #JsonNode * * Frees the resources allocated by @node. */ void json_node_free (JsonNode *node) { if (G_LIKELY (node)) { switch (node->type) { case JSON_NODE_OBJECT: if (node->data.object) json_object_unref (node->data.object); break; case JSON_NODE_ARRAY: if (node->data.array) json_array_unref (node->data.array); break; case JSON_NODE_VALUE: if (node->data.value) json_value_unref (node->data.value); break; case JSON_NODE_NULL: break; } g_slice_free (JsonNode, node); } } /** * json_node_type_name: * @node: a #JsonNode * * Retrieves the user readable name of the data type contained by @node. * * Return value: a string containing the name of the type. The returned string * is owned by the node and should never be modified or freed */ const gchar * json_node_type_name (JsonNode *node) { g_return_val_if_fail (node != NULL, "(null)"); switch (node->type) { case JSON_NODE_OBJECT: case JSON_NODE_ARRAY: case JSON_NODE_NULL: return json_node_type_get_name (node->type); case JSON_NODE_VALUE: return g_type_name (G_VALUE_TYPE (&(node->data.value))); } return "unknown"; } const gchar * json_node_type_get_name (JsonNodeType node_type) { switch (node_type) { case JSON_NODE_OBJECT: return "JsonObject"; case JSON_NODE_ARRAY: return "JsonArray"; case JSON_NODE_NULL: return "NULL"; case JSON_NODE_VALUE: return "Value"; default: g_assert_not_reached (); break; } return "unknown"; } /** * json_node_set_parent: * @node: a #JsonNode * @parent: (transfer none): the parent #JsonNode of @node * * Sets the parent #JsonNode of @node * * Since: 0.8 */ void json_node_set_parent (JsonNode *node, JsonNode *parent) { g_return_if_fail (node != NULL); node->parent = parent; } /** * json_node_get_parent: * @node: a #JsonNode * * Retrieves the parent #JsonNode of @node. * * Return value: (transfer none): the parent node, or %NULL if @node is * the root node */ JsonNode * json_node_get_parent (JsonNode *node) { g_return_val_if_fail (node != NULL, NULL); return node->parent; } /** * json_node_set_string: * @node: a #JsonNode of type %JSON_NODE_VALUE * @value: a string value * * Sets @value as the string content of the @node, replacing any existing * content. */ void json_node_set_string (JsonNode *node, const gchar *value) { g_return_if_fail (node != NULL); g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); if (node->data.value == NULL) node->data.value = json_value_init (json_value_alloc (), JSON_VALUE_STRING); else json_value_init (node->data.value, JSON_VALUE_STRING); json_value_set_string (node->data.value, value); } /** * json_node_get_string: * @node: a #JsonNode of type %JSON_NODE_VALUE * * Gets the string value stored inside a #JsonNode * * Return value: a string value. */ const gchar * json_node_get_string (JsonNode *node) { g_return_val_if_fail (node != NULL, NULL); if (JSON_NODE_TYPE (node) == JSON_NODE_NULL) return NULL; if (JSON_VALUE_HOLDS_STRING (node->data.value)) return json_value_get_string (node->data.value); return NULL; } /** * json_node_dup_string: * @node: a #JsonNode of type %JSON_NODE_VALUE * * Gets a copy of the string value stored inside a #JsonNode * * Return value: (transfer full): a newly allocated string containing a copy * of the #JsonNode contents. Use g_free() to free the allocated resources */ gchar * json_node_dup_string (JsonNode *node) { g_return_val_if_fail (node != NULL, NULL); return g_strdup (json_node_get_string (node)); } /** * json_node_set_int: * @node: a #JsonNode of type %JSON_NODE_VALUE * @value: an integer value * * Sets @value as the integer content of the @node, replacing any existing * content. */ void json_node_set_int (JsonNode *node, gint64 value) { g_return_if_fail (node != NULL); g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); if (node->data.value == NULL) node->data.value = json_value_init (json_value_alloc (), JSON_VALUE_INT); else json_value_init (node->data.value, JSON_VALUE_INT); json_value_set_int (node->data.value, value); } /** * json_node_get_int: * @node: a #JsonNode of type %JSON_NODE_VALUE * * Gets the integer value stored inside a #JsonNode * * Return value: an integer value. */ gint64 json_node_get_int (JsonNode *node) { g_return_val_if_fail (node != NULL, 0); if (JSON_NODE_TYPE (node) == JSON_NODE_NULL) return 0; if (JSON_VALUE_HOLDS_INT (node->data.value)) return json_value_get_int (node->data.value); return 0; } /** * json_node_set_double: * @node: a #JsonNode of type %JSON_NODE_VALUE * @value: a double value * * Sets @value as the double content of the @node, replacing any existing * content. */ void json_node_set_double (JsonNode *node, gdouble value) { g_return_if_fail (node != NULL); g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); if (node->data.value == NULL) node->data.value = json_value_init (json_value_alloc (), JSON_VALUE_DOUBLE); else json_value_init (node->data.value, JSON_VALUE_DOUBLE); json_value_set_double (node->data.value, value); } /** * json_node_get_double: * @node: a #JsonNode of type %JSON_NODE_VALUE * * Gets the double value stored inside a #JsonNode * * Return value: a double value. */ gdouble json_node_get_double (JsonNode *node) { g_return_val_if_fail (node != NULL, 0.0); if (JSON_NODE_TYPE (node) == JSON_NODE_NULL) return 0; if (JSON_VALUE_HOLDS_DOUBLE (node->data.value)) return json_value_get_double (node->data.value); return 0.0; } /** * json_node_set_boolean: * @node: a #JsonNode of type %JSON_NODE_VALUE * @value: a boolean value * * Sets @value as the boolean content of the @node, replacing any existing * content. */ void json_node_set_boolean (JsonNode *node, gboolean value) { g_return_if_fail (node != NULL); g_return_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE); if (node->data.value == NULL) node->data.value = json_value_init (json_value_alloc (), JSON_VALUE_BOOLEAN); else json_value_init (node->data.value, JSON_VALUE_BOOLEAN); json_value_set_boolean (node->data.value, value); } /** * json_node_get_boolean: * @node: a #JsonNode of type %JSON_NODE_VALUE * * Gets the boolean value stored inside a #JsonNode * * Return value: a boolean value. */ gboolean json_node_get_boolean (JsonNode *node) { g_return_val_if_fail (node != NULL, FALSE); if (JSON_NODE_TYPE (node) == JSON_NODE_NULL) return FALSE; if (JSON_VALUE_HOLDS_BOOLEAN (node->data.value)) return json_value_get_boolean (node->data.value); return FALSE; } /** * json_node_get_node_type: * @node: a #JsonNode * * Retrieves the #JsonNodeType of @node * * Return value: the type of the node * * Since: 0.8 */ JsonNodeType json_node_get_node_type (JsonNode *node) { g_return_val_if_fail (node != NULL, JSON_NODE_NULL); return node->type; } /** * json_node_is_null: * @node: a #JsonNode * * Checks whether @node is a %JSON_NODE_NULL * * A null node is not the same as a %NULL #JsonNode * * Return value: %TRUE if the node is null * * Since: 0.8 */ gboolean json_node_is_null (JsonNode *node) { g_return_val_if_fail (node != NULL, TRUE); return node->type == JSON_NODE_NULL; }