summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/json-glib-sections.txt2
-rw-r--r--json-glib/json-object.c113
-rw-r--r--json-glib/json-types-private.h14
-rw-r--r--json-glib/json-types.h8
-rw-r--r--json-glib/tests/object.c58
5 files changed, 185 insertions, 10 deletions
diff --git a/doc/json-glib-sections.txt b/doc/json-glib-sections.txt
index ced06d0..556bed3 100644
--- a/doc/json-glib-sections.txt
+++ b/doc/json-glib-sections.txt
@@ -27,6 +27,8 @@ json_object_foreach_member
JsonObjectIter
json_object_iter_init
json_object_iter_next
+json_object_iter_init_ordered
+json_object_iter_next_ordered
<SUBSECTION>
json_object_set_array_member
diff --git a/json-glib/json-object.c b/json-glib/json-object.c
index 8e6b293..c67b687 100644
--- a/json-glib/json-object.c
+++ b/json-glib/json-object.c
@@ -62,7 +62,8 @@ json_object_new (void)
JsonObject *object;
object = g_slice_new0 (JsonObject);
-
+
+ object->age = 0;
object->ref_count = 1;
object->members = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free,
@@ -176,7 +177,10 @@ object_set_member_internal (JsonObject *object,
gchar *name = g_strdup (member_name);
if (g_hash_table_lookup (object->members, name) == NULL)
- g_queue_push_tail (&object->members_ordered, name);
+ {
+ g_queue_push_tail (&object->members_ordered, name);
+ object->age += 1;
+ }
else
{
GList *l;
@@ -1120,6 +1124,12 @@ json_object_iter_init (JsonObjectIter *iter,
* The order in which members are returned by the iterator is undefined. The
* iterator is invalidated if its #JsonObject is modified during iteration.
*
+ * You must use this function with a #JsonObjectIter initialized with
+ * json_object_iter_init(); using this function with an iterator initialized
+ * with json_object_iter_init_ordered() yields undefined behavior.
+ *
+ * See also: json_object_iter_next_ordered()
+ *
* Returns: %TRUE if @member_name and @member_node are valid; %FALSE if the end
* of the object has been reached
*
@@ -1140,3 +1150,102 @@ json_object_iter_next (JsonObjectIter *iter,
(gpointer *) member_name,
(gpointer *) member_node);
}
+
+/**
+ * json_object_iter_init_ordered:
+ * @iter: an uninitialised #JsonObjectIter
+ * @object: the #JsonObject to iterate over
+ *
+ * Initialise the @iter and associate it with @object.
+ *
+ * |[<!-- language="C" -->
+ * JsonObjectIter iter;
+ * const gchar *member_name;
+ * JsonNode *member_node;
+ *
+ * json_object_iter_init_ordered (&iter, some_object);
+ * while (json_object_iter_next_ordered (&iter, &member_name, &member_node))
+ * {
+ * // Do something with @member_name and @member_node.
+ * }
+ * ]|
+ *
+ * See also: json_object_iter_init()
+ *
+ * Since: 1.6
+ */
+void
+json_object_iter_init_ordered (JsonObjectIter *iter,
+ JsonObject *object)
+{
+ JsonObjectOrderedIterReal *iter_real = (JsonObjectOrderedIterReal *) iter;
+
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (object->ref_count > 0);
+
+ iter_real->object = object;
+ iter_real->cur_member = NULL;
+ iter_real->next_member = NULL;
+ iter_real->age = iter_real->object->age;
+}
+
+/**
+ * json_object_iter_next_ordered:
+ * @iter: a #JsonObjectIter
+ * @member_name: (out callee-allocates) (transfer none) (optional): return
+ * location for the member name, or %NULL to ignore
+ * @member_node: (out callee-allocates) (transfer none) (optional): return
+ * location for the member value, or %NULL to ignore
+ *
+ * Advance @iter and retrieve the next member in the object. If the end of the
+ * object is reached, %FALSE is returned and @member_name and @member_node are
+ * set to invalid values. After that point, the @iter is invalid.
+ *
+ * The order in which members are returned by the iterator is the same order in
+ * which the members were added to the #JsonObject. The iterator is invalidated
+ * if its #JsonObject is modified during iteration.
+ *
+ * You must use this function with a #JsonObjectIter initialized with
+ * json_object_iter_init_ordered(); using this function with an iterator initialized
+ * with json_object_iter_init() yields undefined behavior.
+ *
+ * See also: json_object_iter_next()
+ *
+ * Returns: %TRUE if @member_name and @member_node are valid; %FALSE if the end
+ * of the object has been reached
+ *
+ * Since: 1.6
+ */
+gboolean
+json_object_iter_next_ordered (JsonObjectIter *iter,
+ const gchar **member_name,
+ JsonNode **member_node)
+{
+ JsonObjectOrderedIterReal *iter_real = (JsonObjectOrderedIterReal *) iter;
+ const char *name = NULL;
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (iter_real->object != NULL, FALSE);
+ g_return_val_if_fail (iter_real->object->ref_count > 0, FALSE);
+ g_return_val_if_fail (iter_real->age == iter_real->object->age, FALSE);
+
+ if (iter_real->cur_member == NULL)
+ iter_real->cur_member = iter_real->object->members_ordered.head;
+ else
+ iter_real->cur_member = iter_real->cur_member->next;
+
+ name = iter_real->cur_member != NULL ? iter_real->cur_member->data : NULL;
+
+ if (member_name != NULL)
+ *member_name = name;
+ if (member_node != NULL)
+ {
+ if (name != NULL)
+ *member_node = g_hash_table_lookup (iter_real->object->members, name);
+ else
+ *member_name = NULL;
+ }
+
+ return iter_real->cur_member != NULL;
+}
diff --git a/json-glib/json-types-private.h b/json-glib/json-types-private.h
index bc29e80..264b32b 100644
--- a/json-glib/json-types-private.h
+++ b/json-glib/json-types-private.h
@@ -104,6 +104,7 @@ struct _JsonObject
GQueue members_ordered;
+ int age;
guint immutable_hash; /* valid iff immutable */
volatile gint ref_count;
gboolean immutable : 1;
@@ -118,6 +119,19 @@ typedef struct
G_STATIC_ASSERT (sizeof (JsonObjectIterReal) == sizeof (JsonObjectIter));
+typedef struct
+{
+ JsonObject *object; /* unowned */
+ GList *cur_member;
+ GList *next_member;
+ gpointer priv_pointer[3];
+ int age;
+ int priv_int[1];
+ gboolean priv_boolean;
+} JsonObjectOrderedIterReal;
+
+G_STATIC_ASSERT (sizeof (JsonObjectOrderedIterReal) == sizeof (JsonObjectIter));
+
G_GNUC_INTERNAL
const gchar * json_node_type_get_name (JsonNodeType node_type);
G_GNUC_INTERNAL
diff --git a/json-glib/json-types.h b/json-glib/json-types.h
index 99d1fb9..5e16d4d 100644
--- a/json-glib/json-types.h
+++ b/json-glib/json-types.h
@@ -448,6 +448,14 @@ gboolean json_object_iter_next (JsonObjectIter *iter,
const gchar **member_name,
JsonNode **member_node);
+JSON_AVAILABLE_IN_1_6
+void json_object_iter_init_ordered (JsonObjectIter *iter,
+ JsonObject *object);
+JSON_AVAILABLE_IN_1_6
+gboolean json_object_iter_next_ordered (JsonObjectIter *iter,
+ const char **member_name,
+ JsonNode **member_node);
+
JSON_AVAILABLE_IN_1_0
GType json_array_get_type (void) G_GNUC_CONST;
JSON_AVAILABLE_IN_1_0
diff --git a/json-glib/tests/object.c b/json-glib/tests/object.c
index 772265a..fa2efd2 100644
--- a/json-glib/tests/object.c
+++ b/json-glib/tests/object.c
@@ -106,6 +106,7 @@ test_remove_member (void)
typedef struct _TestForeachFixture
{
gint n_members;
+ gboolean ordered;
} TestForeachFixture;
static const struct {
@@ -128,15 +129,27 @@ verify_foreach (JsonObject *object,
gpointer user_data)
{
TestForeachFixture *fixture = user_data;
- gint i;
- for (i = 0; i < G_N_ELEMENTS (type_verify); i++)
+ if (fixture->ordered)
{
- if (strcmp (member_name, type_verify[i].member_name) == 0)
+ int idx = fixture->n_members;
+
+ g_assert_cmpstr (member_name, ==, type_verify[idx].member_name);
+ g_assert_true (json_node_get_node_type (member_node) == type_verify[idx].member_type);
+ g_assert_true (json_node_get_value_type (member_node) == type_verify[idx].member_gtype);
+ }
+ else
+ {
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (type_verify); i++)
{
- g_assert (json_node_get_node_type (member_node) == type_verify[i].member_type);
- g_assert (json_node_get_value_type (member_node) == type_verify[i].member_gtype);
- break;
+ if (strcmp (member_name, type_verify[i].member_name) == 0)
+ {
+ g_assert_true (json_node_get_node_type (member_node) == type_verify[i].member_type);
+ g_assert_true (json_node_get_value_type (member_node) == type_verify[i].member_gtype);
+ break;
+ }
}
}
@@ -147,7 +160,7 @@ static void
test_foreach_member (void)
{
JsonObject *object = json_object_new ();
- TestForeachFixture fixture = { 0, };
+ TestForeachFixture fixture = { 0, TRUE, };
json_object_set_int_member (object, "integer", 42);
json_object_set_boolean_member (object, "boolean", TRUE);
@@ -167,7 +180,7 @@ static void
test_iter (void)
{
JsonObject *object = NULL;
- TestForeachFixture fixture = { 0, };
+ TestForeachFixture fixture = { 0, FALSE, };
JsonObjectIter iter;
const gchar *member_name;
JsonNode *member_node;
@@ -192,6 +205,34 @@ test_iter (void)
}
static void
+test_ordered_iter (void)
+{
+ JsonObject *object = NULL;
+ TestForeachFixture fixture = { 0, TRUE, };
+ JsonObjectIter iter;
+ const gchar *member_name;
+ JsonNode *member_node;
+
+ object = json_object_new ();
+
+ json_object_set_int_member (object, "integer", 42);
+ json_object_set_boolean_member (object, "boolean", TRUE);
+ json_object_set_string_member (object, "string", "hello");
+ json_object_set_double_member (object, "double", 3.14159);
+ json_object_set_null_member (object, "null");
+ json_object_set_int_member (object, "", 0);
+
+ json_object_iter_init_ordered (&iter, object);
+
+ while (json_object_iter_next_ordered (&iter, &member_name, &member_node))
+ verify_foreach (object, member_name, member_node, &fixture);
+
+ g_assert_cmpint (fixture.n_members, ==, json_object_get_size (object));
+
+ json_object_unref (object);
+}
+
+static void
test_empty_member (void)
{
JsonObject *object = json_object_new ();
@@ -227,6 +268,7 @@ main (int argc,
g_test_add_func ("/object/remove-member", test_remove_member);
g_test_add_func ("/object/foreach-member", test_foreach_member);
g_test_add_func ("/object/iter", test_iter);
+ g_test_add_func ("/object/ordered-iter", test_ordered_iter);
g_test_add_func ("/object/empty-member", test_empty_member);
return g_test_run ();