json-glib r21 - in trunk: . contrib doc doc/reference json-glib json-glib/tests tests



Author: ebassi
Date: Sat Apr 19 16:34:24 2008
New Revision: 21
URL: http://svn.gnome.org/viewvc/json-glib?rev=21&view=rev

Log:
Resync with the development git repository

Added:
   trunk/Makefile.decl
   trunk/json-glib/json-scanner.c
   trunk/json-glib/json-scanner.h
   trunk/json-glib/tests/
   trunk/json-glib/tests/Makefile.am
   trunk/json-glib/tests/array-test.c
   trunk/json-glib/tests/node-test.c
   trunk/json-glib/tests/object-test.c
   trunk/tests/test-generator.c
   trunk/tests/test-parser.c
   trunk/tests/test-serialize-complex.c
   trunk/tests/test-serialize-full.c
   trunk/tests/test-serialize-simple.c
Removed:
   trunk/tests/test-01.c
   trunk/tests/test-02.c
   trunk/tests/test-03.c
   trunk/tests/test-04.c
   trunk/tests/test-05.c
   trunk/tests/test-06.c
   trunk/tests/test-07.c
   trunk/tests/test-08.c
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/README
   trunk/configure.ac
   trunk/contrib/Makefile.am
   trunk/doc/Makefile.am
   trunk/doc/reference/Makefile.am
   trunk/doc/reference/json-glib-sections.txt
   trunk/json-glib/Makefile.am
   trunk/json-glib/json-array.c
   trunk/json-glib/json-generator.c
   trunk/json-glib/json-glib.h
   trunk/json-glib/json-gobject.c
   trunk/json-glib/json-node.c
   trunk/json-glib/json-object.c
   trunk/json-glib/json-parser.c
   trunk/json-glib/json-parser.h
   trunk/tests/Makefile.am

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Sat Apr 19 16:34:24 2008
@@ -1,3 +1,5 @@
+include $(top_srcdir)/Makefile.decl
+
 SUBDIRS = json-glib doc tests
 
 if HAVE_VALA
@@ -8,14 +10,14 @@
 
 pcfiles = json-glib-1.0.pc
 
-%-1.0.pc: %.pc
+json-glib-1.0.pc: json-glib.pc
 	@cp -f $< $@
 
 pkgconfig_DATA = $(pcfiles)
 pkgconfigdir = $(libdir)/pkgconfig
 
-EXTRA_DIST = json-glib.pc.in
+EXTRA_DIST += json-glib.pc.in
 
-CLEANFILES = $(pcfiles)
+CLEANFILES = $(pcfiles) test-report.xml
 
 DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-maintainer-flags

Added: trunk/Makefile.decl
==============================================================================
--- (empty file)
+++ trunk/Makefile.decl	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,61 @@
+# JSON-GLib - JSON reader and writer library
+
+GTESTER        = gtester
+GTESTER_REPORT = gtester-report
+
+# initialize variables for unconditional += appending
+EXTRA_DIST =
+TEST_PROGS =
+
+### testing rules
+
+# test: run all tests in cwd and subdirs
+test:	${TEST_PROGS}
+	@test -z "${TEST_PROGS}" || ${GTESTER} --verbose ${TEST_PROGS}
+	@ for subdir in $(SUBDIRS) . ; do \
+	    test "$$subdir" = "." -o "$$subdir" = "po" || \
+	    ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \
+	  done
+# test-report: run tests in subdirs and generate report
+# perf-report: run tests in subdirs with -m perf and generate report
+# full-report: like test-report: with -m perf and -m slow
+test-report perf-report full-report:	${TEST_PROGS}
+	@test -z "${TEST_PROGS}" || { \
+	  case $@ in \
+	  test-report) test_options="-k";; \
+	  perf-report) test_options="-k -m=perf";; \
+	  full-report) test_options="-k -m=perf -m=slow";; \
+	  esac ; \
+	  if test -z "$$GTESTER_LOGDIR" ; then	\
+	    ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \
+	  elif test -n "${TEST_PROGS}" ; then \
+	    ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \
+	  fi ; \
+	}
+	@ ignore_logdir=true ; \
+	  if test -z "$$GTESTER_LOGDIR" ; then \
+	    GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \
+	    ignore_logdir=false ; \
+	  fi ; \
+	  for subdir in $(SUBDIRS) . ; do \
+	    test "$$subdir" = "." -o "$$subdir" = "po" || \
+	    ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \
+	  done ; \
+	  $$ignore_logdir || { \
+	    echo '<?xml version="1.0"?>'            > $  xml ; \
+	    echo '<report-collection>'             >> $  xml ; \
+	    echo '<info>'                          >> $  xml ; \
+	    echo '  <package>$(PACKAGE)</package>' >> $  xml ; \
+	    echo '  <version>$(VERSION)</version>' >> $  xml ; \
+	    echo '</info>'                         >> $  xml ; \
+	    for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \
+	      sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $  xml ; \
+	    done ; \
+	    echo >> $  xml ; \
+	    echo '</report-collection>' >> $  xml ; \
+	    rm -rf "$$GTESTER_LOGDIR"/ ; \
+	    ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $  xml >$  html ; \
+	  }
+.PHONY: test test-report perf-report full-report
+# run make test as part of make check
+check-local: test

Modified: trunk/README
==============================================================================
--- trunk/README	(original)
+++ trunk/README	Sat Apr 19 16:34:24 2008
@@ -33,6 +33,42 @@
   $ make all
   # make install
 
+BUGS
+----
+
+If you find a bug in JSON-GLib, please create a Bugzilla entry here:
+
+  http://bugzilla.openedhand.com/enter_bug.cgi?product=json-glib
+
+Attaching:
+- the version of JSON-GLib
+  - if it is a development version, the branch of the git repository
+- the JSON data that produced the bug (if any)
+- a small test case, if none of the test units exhibit the behaviour
+- in case of a segmentation fault, a full stack trace with debugging
+  symbols obtained through gdb is greatly appreaciated
+
+HACKING
+-------
+
+JSON-GLib is developed mainly inside a GIT repository available at:
+
+  git://github.com/ebassi/json-glib.git
+
+You can clone the GIT repository with:
+
+  git clone git://github.com/ebassi/json-glib.git
+
+If you want to contribute functionality or bug fixes to JSON-GLib you
+can either notify me to pull from your GIT repository or send me a set
+of patches using:
+
+  git format-patch master -k -s
+
+or:
+
+  git send-email -k -s
+
 AUTHOR
 ------
 JSON-GLib has been written by Emmanuele Bassi

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Sat Apr 19 16:34:24 2008
@@ -15,13 +15,13 @@
 m4_define([lt_age],
           [m4_eval(json_binary_age - json_interface_age)])
 
-m4_define([glib_req_version], [2.12])
+m4_define([glib_req_version], [2.15])
 
-AC_PREREQ(2.59)
+AC_PREREQ([2.59])
 AC_INIT([json-glib], [json_version], [], [json-glib])
 AC_CONFIG_SRCDIR([json-glib/json-glib.h])
 
-AM_INIT_AUTOMAKE([1.7])
+AM_INIT_AUTOMAKE([1.9])
 AM_CONFIG_HEADER([config.h])
 
 AM_DISABLE_STATIC
@@ -113,6 +113,7 @@
         Makefile
         json-glib/Makefile
         json-glib/json-version.h
+        json-glib/tests/Makefile
         tests/Makefile
         doc/Makefile
         doc/reference/Makefile

Modified: trunk/contrib/Makefile.am
==============================================================================
--- trunk/contrib/Makefile.am	(original)
+++ trunk/contrib/Makefile.am	Sat Apr 19 16:34:24 2008
@@ -1,4 +1,6 @@
+include $(top_srcdir)/Makefile.decl
+
 vapidir = @VAPI_DIR@
 vapi_DATA = json-glib-1.0.vapi
 
-EXTRA_DIST = json-test.vala
+EXTRA_DIST += json-test.vala

Modified: trunk/doc/Makefile.am
==============================================================================
--- trunk/doc/Makefile.am	(original)
+++ trunk/doc/Makefile.am	Sat Apr 19 16:34:24 2008
@@ -1 +1,3 @@
+include $(top_srcdir)/Makefile.decl
+
 SUBDIRS = reference

Modified: trunk/doc/reference/Makefile.am
==============================================================================
--- trunk/doc/reference/Makefile.am	(original)
+++ trunk/doc/reference/Makefile.am	Sat Apr 19 16:34:24 2008
@@ -1,4 +1,5 @@
 ## Process this file with automake to produce Makefile.in
+include $(top_srcdir)/Makefile.decl
 
 # We require automake 1.6 at least.
 AUTOMAKE_OPTIONS = 1.6

Modified: trunk/doc/reference/json-glib-sections.txt
==============================================================================
--- trunk/doc/reference/json-glib-sections.txt	(original)
+++ trunk/doc/reference/json-glib-sections.txt	Sat Apr 19 16:34:24 2008
@@ -102,7 +102,6 @@
 
 <SUBSECTION>
 json_parser_get_root
-json_parser_peek_root
 
 <SUBSECTION>
 json_parser_get_current_line

Modified: trunk/json-glib/Makefile.am
==============================================================================
--- trunk/json-glib/Makefile.am	(original)
+++ trunk/json-glib/Makefile.am	Sat Apr 19 16:34:24 2008
@@ -1,3 +1,9 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = . tests
+
+DIST_SUBDIRS = tests
+
 NULL =
 
 INCLUDES = \
@@ -40,6 +46,7 @@
 	$(top_srcdir)/json-glib/json-generator.h \
 	$(top_srcdir)/json-glib/json-gobject.h \
 	$(top_srcdir)/json-glib/json-parser.h \
+	$(top_srcdir)/json-glib/json-scanner.h \
 	$(top_srcdir)/json-glib/json-types.h \
 	$(top_srcdir)/json-glib/json-version.h \
 	$(NULL)
@@ -53,6 +60,7 @@
 	json-node.c \
 	json-object.c \
 	json-parser.c \
+	json-scanner.c \
 	$(NULL)
 
 lib_LTLIBRARIES = libjson-glib-1.0.la
@@ -75,5 +83,6 @@
 
 DISTCLEANFILES = json-version.h
 
-EXTRA_DIST = json-version.h.in json-glib.h json-marshal.list
+EXTRA_DIST += json-version.h.in json-glib.h json-marshal.list
 
+TESTS_ENVIRONMENT = srcdir="$(srcdir)" json_all_c_sources="$(source_c)"

Modified: trunk/json-glib/json-array.c
==============================================================================
--- trunk/json-glib/json-array.c	(original)
+++ trunk/json-glib/json-array.c	Sat Apr 19 16:34:24 2008
@@ -17,7 +17,9 @@
  *   Emmanuele Bassi  <ebassi openedhand com>
  */
 
+#ifdef HAVE_CONFIG_H
 #include "config.h"
+#endif
 
 #include "json-types.h"
 

Modified: trunk/json-glib/json-generator.c
==============================================================================
--- trunk/json-glib/json-generator.c	(original)
+++ trunk/json-glib/json-generator.c	Sat Apr 19 16:34:24 2008
@@ -25,7 +25,9 @@
  * put it into a buffer or a file.
  */
 
+#ifdef HAVE_CONFIG_H
 #include "config.h"
+#endif
 
 #include <stdlib.h>
 #include <string.h>
@@ -329,16 +331,19 @@
         case JSON_NODE_VALUE:
           value = dump_value (generator, sub_level, NULL, cur);
           g_string_append (buffer, value);
+          g_free (value);
           break;
 
         case JSON_NODE_ARRAY:
           value = dump_array (generator, sub_level, NULL, json_node_get_array (cur), NULL);
           g_string_append (buffer, value);
+          g_free (value);
           break;
 
         case JSON_NODE_OBJECT:
           value = dump_object (generator, sub_level, NULL, json_node_get_object (cur), NULL);
           g_string_append (buffer, value);
+          g_free (value);
           break;
         }
 
@@ -421,18 +426,21 @@
         case JSON_NODE_VALUE:
           value = dump_value (generator, sub_level, member_name, cur);
           g_string_append (buffer, value);
+          g_free (value);
           break;
 
         case JSON_NODE_ARRAY:
           value = dump_array (generator, sub_level, member_name,
                               json_node_get_array (cur), NULL);
           g_string_append (buffer, value);
+          g_free (value);
           break;
 
         case JSON_NODE_OBJECT:
           value = dump_object (generator, sub_level, member_name,
                                json_node_get_object (cur), NULL);
           g_string_append (buffer, value);
+          g_free (value);
           break;
         }
 

Modified: trunk/json-glib/json-glib.h
==============================================================================
--- trunk/json-glib/json-glib.h	(original)
+++ trunk/json-glib/json-glib.h	Sat Apr 19 16:34:24 2008
@@ -2,6 +2,7 @@
 #define __JSON_GLIB_H__
 
 #include <json-glib/json-types.h>
+#include <json-glib/json-scanner.h>
 #include <json-glib/json-generator.h>
 #include <json-glib/json-parser.h>
 #include <json-glib/json-version.h>

Modified: trunk/json-glib/json-gobject.c
==============================================================================
--- trunk/json-glib/json-gobject.c	(original)
+++ trunk/json-glib/json-gobject.c	Sat Apr 19 16:34:24 2008
@@ -32,7 +32,9 @@
  * and its virtual functions.
  */
 
+#ifdef HAVE_CONFIG_H
 #include "config.h"
+#endif
 
 #include <string.h>
 #include <stdlib.h>
@@ -41,94 +43,6 @@
 #include "json-parser.h"
 #include "json-generator.h"
 
-GType
-json_serializable_get_type (void)
-{
-  static GType iface_type = 0;
-
-  if (!iface_type)
-    iface_type =
-      g_type_register_static_simple (G_TYPE_INTERFACE, "JsonSerializable",
-                                     sizeof (JsonSerializableIface),
-                                     NULL, 0, NULL, 0);
-
-  return iface_type;
-}
-
-/**
- * json_serializable_serialize_property:
- * @serializable: a #JsonSerializable object
- * @property_name: the name of the property
- * @value: the value of the property
- * @pspec: a #GParamSpec
- *
- * Asks a #JsonSerializable implementation to serialize a #GObject
- * property into a #JsonNode object.
- *
- * Return value: a #JsonNode containing the serialize property
- */
-JsonNode *
-json_serializable_serialize_property (JsonSerializable *serializable,
-                                      const gchar      *property_name,
-                                      const GValue     *value,
-                                      GParamSpec       *pspec)
-{
-  JsonSerializableIface *iface;
-
-  g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), NULL);
-  g_return_val_if_fail (property_name != NULL, NULL);
-  g_return_val_if_fail (value != NULL, NULL);
-  g_return_val_if_fail (pspec != NULL, NULL);
-
-  iface = JSON_SERIALIZABLE_GET_IFACE (serializable);
-  if (!iface->serialize_property)
-    return json_node_new (JSON_NODE_NULL);
-
-  return iface->serialize_property (serializable, property_name, value, pspec);
-}
-
-/**
- * json_serializable_deserialize_property:
- * @serializable: a #JsonSerializable
- * @property_name: the name of the property
- * @value: a pointer to an uninitialized #GValue
- * @pspec: a #GParamSpec
- * @property_node: a #JsonNode containing the serialized property
- *
- * Asks a #JsonSerializable implementation to deserialize the
- * property contained inside @property_node into @value.
- *
- * Return value: %TRUE if the property was successfully deserialized.
- */
-gboolean
-json_serializable_deserialize_property (JsonSerializable *serializable,
-                                        const gchar      *property_name,
-                                        GValue           *value,
-                                        GParamSpec       *pspec,
-                                        JsonNode         *property_node)
-{
-  JsonSerializableIface *iface;
-
-  g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), FALSE);
-  g_return_val_if_fail (property_name != NULL, FALSE);
-  g_return_val_if_fail (value != NULL, FALSE);
-  g_return_val_if_fail (pspec != NULL, FALSE);
-  g_return_val_if_fail (property_node != NULL, FALSE);
-
-  iface = JSON_SERIALIZABLE_GET_IFACE (serializable);
-  if (!iface->deserialize_property)
-    {
-      g_param_value_defaults (pspec, value);
-      return TRUE;
-    }
-
-  return iface->deserialize_property (serializable,
-                                      property_name,
-                                      value,
-                                      pspec,
-                                      property_node);
-}
-
 static gboolean
 enum_from_string (GType        type,
                   const gchar *string,
@@ -375,6 +289,8 @@
           retval = FALSE;
           break;
         }
+
+      g_value_unset (&node_value);
       break;
 
     case JSON_NODE_NULL:
@@ -493,6 +409,124 @@
 }
 
 /**
+ * json_serializable_serialize_property:
+ * @serializable: a #JsonSerializable object
+ * @property_name: the name of the property
+ * @value: the value of the property
+ * @pspec: a #GParamSpec
+ *
+ * Asks a #JsonSerializable implementation to serialize a #GObject
+ * property into a #JsonNode object.
+ *
+ * Return value: a #JsonNode containing the serialize property
+ */
+JsonNode *
+json_serializable_serialize_property (JsonSerializable *serializable,
+                                      const gchar      *property_name,
+                                      const GValue     *value,
+                                      GParamSpec       *pspec)
+{
+  JsonSerializableIface *iface;
+
+  g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), NULL);
+  g_return_val_if_fail (property_name != NULL, NULL);
+  g_return_val_if_fail (value != NULL, NULL);
+  g_return_val_if_fail (pspec != NULL, NULL);
+
+  iface = JSON_SERIALIZABLE_GET_IFACE (serializable);
+
+  return iface->serialize_property (serializable, property_name, value, pspec);
+}
+
+/**
+ * json_serializable_deserialize_property:
+ * @serializable: a #JsonSerializable
+ * @property_name: the name of the property
+ * @value: a pointer to an uninitialized #GValue
+ * @pspec: a #GParamSpec
+ * @property_node: a #JsonNode containing the serialized property
+ *
+ * Asks a #JsonSerializable implementation to deserialize the
+ * property contained inside @property_node into @value.
+ *
+ * Return value: %TRUE if the property was successfully deserialized.
+ */
+gboolean
+json_serializable_deserialize_property (JsonSerializable *serializable,
+                                        const gchar      *property_name,
+                                        GValue           *value,
+                                        GParamSpec       *pspec,
+                                        JsonNode         *property_node)
+{
+  JsonSerializableIface *iface;
+
+  g_return_val_if_fail (JSON_IS_SERIALIZABLE (serializable), FALSE);
+  g_return_val_if_fail (property_name != NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+  g_return_val_if_fail (pspec != NULL, FALSE);
+  g_return_val_if_fail (property_node != NULL, FALSE);
+
+  iface = JSON_SERIALIZABLE_GET_IFACE (serializable);
+
+  return iface->deserialize_property (serializable,
+                                      property_name,
+                                      value,
+                                      pspec,
+                                      property_node);
+}
+
+static gboolean
+json_serializable_real_deserialize (JsonSerializable *serializable,
+                                    const gchar      *name,
+                                    GValue           *value,
+                                    GParamSpec       *pspec,
+                                    JsonNode         *node)
+{
+  return json_deserialize_pspec (value, pspec, node);
+}
+
+static JsonNode *
+json_serializable_real_serialize (JsonSerializable *serializable,
+                                  const gchar      *name,
+                                  const GValue     *value,
+                                  GParamSpec       *pspec)
+{
+  return json_serialize_pspec (value, pspec);
+}
+
+static void
+json_serializable_base_init (gpointer g_class,
+                             gpointer data)
+{
+  static gboolean is_initialized = FALSE;
+
+  if (G_UNLIKELY (!is_initialized))
+    {
+      JsonSerializableIface *iface = g_class;
+
+      iface->serialize_property = json_serializable_real_serialize;
+      iface->deserialize_property = json_serializable_real_deserialize;
+
+      is_initialized = TRUE;
+    }
+}
+
+GType
+json_serializable_get_type (void)
+{
+  static GType iface_type = 0;
+
+  if (!iface_type)
+    iface_type =
+      g_type_register_static_simple (G_TYPE_INTERFACE, "JsonSerializable",
+                                     sizeof (JsonSerializableIface),
+                                     json_serializable_base_init,
+                                     0, NULL, 0);
+
+  return iface_type;
+}
+
+/**
  * json_construct_gobject:
  * @gtype: the #GType of object to construct
  * @data: a JSON data stream
@@ -547,7 +581,7 @@
     }
 
   root = json_parser_get_root (parser);
-  if (JSON_NODE_TYPE (root) != JSON_NODE_OBJECT)
+  if (root == NULL || JSON_NODE_TYPE (root) != JSON_NODE_OBJECT)
     {
       g_set_error (error, JSON_PARSER_ERROR,
                    JSON_PARSER_ERROR_PARSE,
@@ -618,8 +652,6 @@
 
   g_list_free (members);
 
-  json_node_free (root);
-
   g_object_thaw_notify (retval);
 
   g_type_class_unref (klass);

Modified: trunk/json-glib/json-node.c
==============================================================================
--- trunk/json-glib/json-node.c	(original)
+++ trunk/json-glib/json-node.c	Sat Apr 19 16:34:24 2008
@@ -17,7 +17,9 @@
  *   Emmanuele Bassi  <ebassi openedhand com>
  */
 
+#ifdef HAVE_CONFIG_H
 #include "config.h"
+#endif
 
 #include <glib.h>
 
@@ -48,7 +50,7 @@
   static GType node_type = 0;
 
   if (G_UNLIKELY (node_type == 0))
-    node_type = g_boxed_type_register_static ("JsonNode",
+    node_type = g_boxed_type_register_static (g_intern_static_string ("JsonNode"),
                                               (GBoxedCopyFunc) json_node_copy,
                                               (GBoxedFreeFunc) json_node_free);
 
@@ -103,9 +105,10 @@
 {
   JsonNode *data;
 
-  g_return_val_if_fail (type >= JSON_NODE_OBJECT && type <= JSON_NODE_NULL, NULL);
+  g_return_val_if_fail (type >= JSON_NODE_OBJECT &&
+                        type <= JSON_NODE_NULL, NULL);
 
-  data = g_slice_new (JsonNode);
+  data = g_slice_new0 (JsonNode);
   data->type = type;
 
   return data;
@@ -127,7 +130,7 @@
 
   g_return_val_if_fail (node != NULL, NULL);
 
-  copy = g_slice_new (JsonNode);
+  copy = g_slice_new0 (JsonNode);
   *copy = *node;
 
   switch (copy->type)
@@ -489,7 +492,9 @@
 json_node_get_string (JsonNode *node)
 {
   g_return_val_if_fail (node != NULL, NULL);
-  g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, NULL);
+
+  if (JSON_NODE_TYPE (node) == JSON_NODE_NULL)
+    return NULL;
 
   if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_STRING)
     return g_value_get_string (&(node->data.value));
@@ -509,7 +514,10 @@
 gchar *
 json_node_dup_string (JsonNode *node)
 {
-  g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, NULL);
+  g_return_val_if_fail (node != NULL, NULL);
+
+  if (JSON_NODE_TYPE (node) == JSON_NODE_NULL)
+    return NULL;
 
   if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_STRING)
     return g_value_dup_string (&(node->data.value));
@@ -559,7 +567,9 @@
 json_node_get_int (JsonNode *node)
 {
   g_return_val_if_fail (node != NULL, 0);
-  g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, 0);
+
+  if (JSON_NODE_TYPE (node) == JSON_NODE_NULL)
+    return 0;
 
   if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_INT)
     return g_value_get_int (&(node->data.value));
@@ -609,7 +619,9 @@
 json_node_get_double (JsonNode *node)
 {
   g_return_val_if_fail (node != NULL, 0.0);
-  g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, 0.0);
+
+  if (JSON_NODE_TYPE (node) == JSON_NODE_NULL)
+    return 0;
 
   if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_DOUBLE)
     return g_value_get_double (&(node->data.value));
@@ -659,11 +671,12 @@
 json_node_get_boolean (JsonNode *node)
 {
   g_return_val_if_fail (node != NULL, FALSE);
-  g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, FALSE);
+
+  if (JSON_NODE_TYPE (node) == JSON_NODE_NULL)
+    return FALSE;
 
   if (G_VALUE_TYPE (&(node->data.value)) == G_TYPE_BOOLEAN)
     return g_value_get_boolean (&(node->data.value));
 
   return FALSE;
 }
-

Modified: trunk/json-glib/json-object.c
==============================================================================
--- trunk/json-glib/json-object.c	(original)
+++ trunk/json-glib/json-object.c	Sat Apr 19 16:34:24 2008
@@ -17,7 +17,9 @@
  *   Emmanuele Bassi  <ebassi openedhand com>
  */
 
+#ifdef HAVE_CONFIG_H
 #include "config.h"
+#endif
 
 #include <glib.h>
 
@@ -330,7 +332,7 @@
   g_return_val_if_fail (member_name != NULL, FALSE);
 
   name = g_strdelimit (g_strdup (member_name), G_STR_DELIMITERS, '_');
-  retval = (g_hash_table_lookup (object->members, member_name) != NULL);
+  retval = (g_hash_table_lookup (object->members, name) != NULL);
   g_free (name);
 
   return retval;

Modified: trunk/json-glib/json-parser.c
==============================================================================
--- trunk/json-glib/json-parser.c	(original)
+++ trunk/json-glib/json-parser.c	Sat Apr 19 16:34:24 2008
@@ -33,6 +33,7 @@
 
 #include "json-marshal.h"
 #include "json-parser.h"
+#include "json-scanner.h"
 
 GQuark
 json_parser_error_quark (void)
@@ -48,7 +49,7 @@
   JsonNode *root;
   JsonNode *current_node;
 
-  GScanner *scanner;
+  JsonScanner *scanner;
 
   GError *last_error;
 
@@ -56,45 +57,6 @@
   gchar *variable_name;
 };
 
-static const GScannerConfig json_scanner_config =
-{
-  ( " \t\r\n" )		/* cset_skip_characters */,
-  (
-   "_"
-   G_CSET_a_2_z
-   G_CSET_A_2_Z
-  )			/* cset_identifier_first */,
-  (
-   G_CSET_DIGITS
-   "-_"
-   G_CSET_a_2_z
-   G_CSET_A_2_Z
-  )			/* cset_identifier_nth */,
-  ( "#\n" )		/* cpair_comment_single */,
-  TRUE			/* case_sensitive */,
-  TRUE			/* skip_comment_multi */,
-  TRUE			/* skip_comment_single */,
-  FALSE			/* scan_comment_multi */,
-  TRUE			/* scan_identifier */,
-  TRUE			/* scan_identifier_1char */,
-  FALSE			/* scan_identifier_NULL */,
-  TRUE			/* scan_symbols */,
-  TRUE			/* scan_binary */,
-  TRUE			/* scan_octal */,
-  TRUE			/* scan_float */,
-  TRUE			/* scan_hex */,
-  TRUE			/* scan_hex_dollar */,
-  TRUE			/* scan_string_sq */,
-  TRUE			/* scan_string_dq */,
-  TRUE			/* numbers_2_int */,
-  FALSE			/* int_2_float */,
-  FALSE			/* identifier_2_string */,
-  TRUE			/* char_2_token */,
-  TRUE			/* symbol_2_token */,
-  FALSE			/* scope_0_fallback */,
-};
-
-
 static const gchar symbol_names[] =
   "true\0"
   "false\0"
@@ -134,22 +96,18 @@
 G_DEFINE_TYPE (JsonParser, json_parser, G_TYPE_OBJECT);
 
 static guint json_parse_array  (JsonParser *parser,
-                                GScanner   *scanner,
+                                JsonScanner   *scanner,
                                 gboolean    nested);
 static guint json_parse_object (JsonParser *parser,
-                                GScanner   *scanner,
+                                JsonScanner   *scanner,
                                 gboolean    nested);
 
-static void
-json_parser_dispose (GObject *gobject)
+static inline void
+json_parser_clear (JsonParser *parser)
 {
-  JsonParserPrivate *priv = JSON_PARSER_GET_PRIVATE (gobject);
+  JsonParserPrivate *priv = parser->priv;
 
-  if (priv->root)
-    {
-      json_node_free (priv->root);
-      priv->root = NULL;
-    }
+  g_free (priv->variable_name);
 
   if (priv->last_error)
     {
@@ -157,7 +115,17 @@
       priv->last_error = NULL;
     }
 
-  g_free (priv->variable_name);
+  if (priv->root)
+    {
+      json_node_free (priv->root);
+      priv->root = NULL;
+    }
+}
+
+static void
+json_parser_dispose (GObject *gobject)
+{
+  json_parser_clear (JSON_PARSER (gobject));
 
   G_OBJECT_CLASS (json_parser_parent_class)->dispose (gobject);
 }
@@ -340,7 +308,7 @@
 
 static guint
 json_parse_array (JsonParser *parser,
-                  GScanner   *scanner,
+                  JsonScanner   *scanner,
                   gboolean    nested)
 {
   JsonParserPrivate *priv = parser->priv;
@@ -350,7 +318,7 @@
   if (!nested)
     {
       /* the caller already swallowed the opening '[' */
-      token = g_scanner_get_next_token (scanner);
+      token = json_scanner_get_next_token (scanner);
       if (token != G_TOKEN_LEFT_BRACE)
         return G_TOKEN_LEFT_BRACE;
     }
@@ -359,7 +327,7 @@
 
   array = json_array_new ();
 
-  token = g_scanner_get_next_token (scanner);
+  token = json_scanner_get_next_token (scanner);
   while (token != G_TOKEN_RIGHT_BRACE)
     {
       JsonNode *node = NULL;
@@ -368,7 +336,7 @@
       if (token == G_TOKEN_COMMA)
         {
           /* swallow the comma */
-          token = g_scanner_get_next_token (scanner);
+          token = json_scanner_get_next_token (scanner);
           continue;
         }
 
@@ -398,7 +366,7 @@
                          array,
                          json_array_get_length (array));
 
-          token = g_scanner_get_next_token (scanner);
+          token = json_scanner_get_next_token (scanner);
           if (token == G_TOKEN_RIGHT_BRACE)
             break;
 
@@ -434,7 +402,7 @@
                          array,
                          json_array_get_length (array));
 
-          token = g_scanner_get_next_token (scanner);
+          token = json_scanner_get_next_token (scanner);
           if (token == G_TOKEN_RIGHT_BRACE)
             break;
 
@@ -446,13 +414,13 @@
 
       if (token == '-')
         {
-          guint next_token = g_scanner_peek_next_token (scanner);
+          guint next_token = json_scanner_peek_next_token (scanner);
 
           if (next_token == G_TOKEN_INT ||
               next_token == G_TOKEN_FLOAT)
             {
               negative = TRUE;
-              token = g_scanner_get_next_token (scanner);
+              token = json_scanner_get_next_token (scanner);
             }
           else
             {
@@ -504,7 +472,7 @@
                          json_array_get_length (array));
         }
 
-      token = g_scanner_get_next_token (scanner);
+      token = json_scanner_get_next_token (scanner);
       if (token != G_TOKEN_COMMA && token != G_TOKEN_RIGHT_BRACE)
         return G_TOKEN_RIGHT_BRACE;
     }
@@ -518,7 +486,7 @@
 
 static guint
 json_parse_object (JsonParser *parser,
-                   GScanner   *scanner,
+                   JsonScanner   *scanner,
                    gboolean    nested)
 {
   JsonParserPrivate *priv = parser->priv;
@@ -528,7 +496,7 @@
   if (!nested)
     {
       /* the caller already swallowed the opening '{' */
-      token = g_scanner_get_next_token (scanner);
+      token = json_scanner_get_next_token (scanner);
       if (token != G_TOKEN_LEFT_CURLY)
         return G_TOKEN_LEFT_CURLY;
     }
@@ -537,7 +505,7 @@
 
   object = json_object_new ();
 
-  token = g_scanner_get_next_token (scanner);
+  token = json_scanner_get_next_token (scanner);
   while (token != G_TOKEN_RIGHT_CURLY)
     {
       JsonNode *node = NULL;
@@ -547,7 +515,7 @@
       if (token == G_TOKEN_COMMA)
         {
           /* swallow the comma */
-          token = g_scanner_get_next_token (scanner);
+          token = json_scanner_get_next_token (scanner);
           continue;
         }
 
@@ -555,7 +523,7 @@
         {
           name = g_strdup (scanner->value.v_string);
 
-          token = g_scanner_get_next_token (scanner);
+          token = json_scanner_get_next_token (scanner);
           if (token != ':')
             {
               g_free (name);
@@ -565,7 +533,7 @@
           else
             {
               /* swallow the colon */
-              token = g_scanner_get_next_token (scanner);
+              token = json_scanner_get_next_token (scanner);
             }
         }
 
@@ -606,7 +574,7 @@
 
           g_free (name);
 
-          token = g_scanner_get_next_token (scanner);
+          token = json_scanner_get_next_token (scanner);
           if (token == G_TOKEN_RIGHT_CURLY)
             break;
 
@@ -644,7 +612,7 @@
 
           g_free (name);
 
-          token = g_scanner_get_next_token (scanner);
+          token = json_scanner_get_next_token (scanner);
           if (token == G_TOKEN_RIGHT_CURLY)
             break;
 
@@ -656,12 +624,12 @@
 
       if (token == '-')
         {
-          guint next_token = g_scanner_peek_next_token (scanner);
+          guint next_token = json_scanner_peek_next_token (scanner);
 
           if (next_token == G_TOKEN_INT || next_token == G_TOKEN_FLOAT)
             {
               negative = TRUE;
-              token = g_scanner_get_next_token (scanner);
+              token = json_scanner_get_next_token (scanner);
             }
           else
             {
@@ -715,7 +683,7 @@
 
       g_free (name);
 
-      token = g_scanner_get_next_token (scanner);
+      token = json_scanner_get_next_token (scanner);
       if (token != G_TOKEN_COMMA && token != G_TOKEN_RIGHT_CURLY)
         return G_TOKEN_RIGHT_CURLY;
     }
@@ -729,12 +697,12 @@
 
 static guint
 json_parse_statement (JsonParser *parser,
-                      GScanner   *scanner)
+                      JsonScanner   *scanner)
 {
   JsonParserPrivate *priv = parser->priv;
   guint token;
 
-  token = g_scanner_peek_next_token (scanner);
+  token = json_scanner_peek_next_token (scanner);
   switch (token)
     {
     case G_TOKEN_LEFT_CURLY:
@@ -756,17 +724,17 @@
         gchar *name;
 
         /* swallow the 'var' token... */
-        token = g_scanner_get_next_token (scanner);
+        token = json_scanner_get_next_token (scanner);
 
         /* ... swallow the variable name... */
-        next_token = g_scanner_get_next_token (scanner);
+        next_token = json_scanner_get_next_token (scanner);
         if (next_token != G_TOKEN_IDENTIFIER)
           return G_TOKEN_IDENTIFIER;
 
         name = g_strdup (scanner->value.v_identifier);
 
         /* ... and finally swallow the '=' */
-        next_token = g_scanner_get_next_token (scanner);
+        next_token = json_scanner_get_next_token (scanner);
         if (next_token != '=')
           return '=';
 
@@ -776,10 +744,10 @@
         token = json_parse_statement (parser, scanner);
 
         /* remove the trailing semi-colon */
-        next_token = g_scanner_peek_next_token (scanner);
+        next_token = json_scanner_peek_next_token (scanner);
         if (next_token == ';')
           {
-            token = g_scanner_get_next_token (scanner);
+            token = json_scanner_get_next_token (scanner);
             return G_TOKEN_NONE;
           }
 
@@ -802,14 +770,14 @@
       {
         guint next_token;
         
-        token = g_scanner_get_next_token (scanner);
-        next_token = g_scanner_peek_next_token (scanner);
+        token = json_scanner_get_next_token (scanner);
+        next_token = json_scanner_peek_next_token (scanner);
 
         if (next_token == G_TOKEN_INT || next_token == G_TOKEN_FLOAT)
           {
             priv->root = priv->current_node = json_node_new (JSON_NODE_VALUE);
             
-            token = g_scanner_get_next_token (scanner);
+            token = json_scanner_get_next_token (scanner);
             switch (token)
               {
               case G_TOKEN_INT:
@@ -844,13 +812,13 @@
       return G_TOKEN_NONE;
 
     default:
-      g_scanner_get_next_token (scanner);
+      json_scanner_get_next_token (scanner);
       return G_TOKEN_SYMBOL;
     }
 }
 
 static void
-json_scanner_msg_handler (GScanner *scanner,
+json_scanner_msg_handler (JsonScanner *scanner,
                           gchar    *message,
                           gboolean  is_error)
 {
@@ -873,15 +841,23 @@
     g_warning ("Line %d: %s", scanner->line, message);
 }
 
-static GScanner *
-json_scanner_new (JsonParser *parser)
+static JsonScanner *
+json_scanner_create (JsonParser *parser)
 {
-  GScanner *scanner;
+  JsonScanner *scanner;
+  gint i;
 
-  scanner = g_scanner_new (&json_scanner_config);
+  scanner = json_scanner_new ();
   scanner->msg_handler = json_scanner_msg_handler;
   scanner->user_data = parser;
 
+  for (i = 0; i < n_symbols; i++)
+    {
+      json_scanner_scope_add_symbol (scanner, 0,
+                                     symbol_names + symbols[i].name_offset,
+                                     GINT_TO_POINTER (symbols[i].token));
+    }
+
   return scanner;
 }
 
@@ -961,10 +937,11 @@
 gboolean
 json_parser_load_from_data (JsonParser   *parser,
                             const gchar  *data,
-                            gsize         length,
+                            gssize        length,
                             GError      **error)
 {
-  GScanner *scanner;
+  JsonParserPrivate *priv;
+  JsonScanner *scanner;
   gboolean done;
   gboolean retval = TRUE;
   gint i;
@@ -972,24 +949,15 @@
   g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE);
   g_return_val_if_fail (data != NULL, FALSE);
 
+  json_parser_clear (parser);
+
   if (length < 0)
     length = strlen (data);
 
-  if (parser->priv->root)
-    {
-      json_node_free (parser->priv->root);
-      parser->priv->root = NULL;
-    }
-
-  scanner = json_scanner_new (parser);
-  g_scanner_input_text (scanner, data, strlen (data));
+  priv = parser->priv;
 
-  for (i = 0; i < n_symbols; i++)
-    {
-      g_scanner_scope_add_symbol (scanner, 0,
-                                  symbol_names + symbols[i].name_offset,
-                                  GINT_TO_POINTER (symbols[i].token));
-    }
+  scanner = json_scanner_create (parser);
+  json_scanner_input_text (scanner, data, length);
 
   parser->priv->scanner = scanner;
 
@@ -998,7 +966,7 @@
   done = FALSE;
   while (!done)
     {
-      if (g_scanner_peek_next_token (scanner) == G_TOKEN_EOF)
+      if (json_scanner_peek_next_token (scanner) == G_TOKEN_EOF)
         done = TRUE;
       else
         {
@@ -1040,18 +1008,18 @@
               /* this will emit the ::error signal via the custom
                * message handler we install
                */
-              g_scanner_unexp_token (scanner, expected_token,
-                                     NULL, "keyword",
-                                     symbol_name, msg,
-                                     TRUE);
+              json_scanner_unexp_token (scanner, expected_token,
+                                        NULL, "keyword",
+                                        symbol_name, msg,
+                                        TRUE);
 
               /* and this will propagate the error we create in the
                * same message handler
                */
-              if (parser->priv->last_error)
+              if (priv->last_error)
                 {
-                  g_propagate_error (error, parser->priv->last_error);
-                  parser->priv->last_error = NULL;
+                  g_propagate_error (error, priv->last_error);
+                  priv->last_error = NULL;
                 }
 
               retval = FALSE;
@@ -1062,52 +1030,31 @@
         }
     }
 
-  g_scanner_destroy (scanner);
-  parser->priv->scanner = NULL;
-  parser->priv->current_node = NULL;
-
   g_signal_emit (parser, parser_signals[PARSE_END], 0);
 
+  /* remove the scanner */
+  json_scanner_destroy (scanner);
+  priv->scanner = NULL;
+  priv->current_node = NULL;
+
   return retval;
 }
 
 /**
- * json_parser_peek_root:
+ * json_parser_get_root:
  * @parser: a #JsonParser
  *
  * Retrieves the top level node from the parsed JSON stream.
  *
  * Return value: the root #JsonNode . The returned node is owned by
  *   the #JsonParser and should never be modified or freed.
- *
- * Since: 0.6
- */
-JsonNode *
-json_parser_peek_root (JsonParser *parser)
-{
-  g_return_val_if_fail (JSON_IS_PARSER (parser), NULL);
-
-  return parser->priv->root;
-}
-
-/**
- * json_parser_get_root:
- * @parser: a #JsonParser
- *
- * Retrieves a copy of the top level node from the parsed JSON stream.
- *
- * Return value: a newly allocated copy of the root #JsonNode. Use
- *   json_node_free() to free its allocated resources.
  */
 JsonNode *
 json_parser_get_root (JsonParser *parser)
 {
   g_return_val_if_fail (JSON_IS_PARSER (parser), NULL);
 
-  if (!parser->priv->root)
-    return NULL;
-
-  return json_node_copy (parser->priv->root);
+  return parser->priv->root;
 }
 
 /**
@@ -1116,7 +1063,11 @@
  *
  * Retrieves the line currently parsed, starting from 1.
  *
- * Return value: the currently parsed line.
+ * This function has defined behaviour only while parsing; calling this
+ * function from outside the signal handlers emitted by #JsonParser will
+ * yield 0.
+ *
+ * Return value: the currently parsed line, or 0.
  */
 guint
 json_parser_get_current_line (JsonParser *parser)
@@ -1124,7 +1075,7 @@
   g_return_val_if_fail (JSON_IS_PARSER (parser), 0);
 
   if (parser->priv->scanner)
-    return g_scanner_cur_line (parser->priv->scanner);
+    return json_scanner_cur_line (parser->priv->scanner);
 
   return 0;
 }
@@ -1136,7 +1087,11 @@
  * Retrieves the current position inside the current line, starting
  * from 0.
  *
- * Return value: the position in the current line
+ * This function has defined behaviour only while parsing; calling this
+ * function from outside the signal handlers emitted by #JsonParser will
+ * yield 0.
+ *
+ * Return value: the position in the current line, or 0.
  */
 guint
 json_parser_get_current_pos (JsonParser *parser)
@@ -1144,7 +1099,7 @@
   g_return_val_if_fail (JSON_IS_PARSER (parser), 0);
 
   if (parser->priv->scanner)
-    return g_scanner_cur_line (parser->priv->scanner);
+    return json_scanner_cur_line (parser->priv->scanner);
 
   return 0;
 }
@@ -1157,7 +1112,7 @@
  * A JSON data stream might sometimes contain an assignment, even though
  * it would technically constitute a violation of the RFC. #JsonParser
  * will ignore the left hand identifier and parse the right hand value
- * of the assignment. It will record, though, the existence of the
+ * of the assignment. #JsonParser will record, though, the existence of the
  * assignment in the data stream and the variable name used.
  *
  * Return value: %TRUE if there was an assignment, %FALSE otherwise. If

Modified: trunk/json-glib/json-parser.h
==============================================================================
--- trunk/json-glib/json-parser.h	(original)
+++ trunk/json-glib/json-parser.h	Sat Apr 19 16:34:24 2008
@@ -51,17 +51,6 @@
   JSON_PARSER_ERROR_UNKNOWN
 } JsonParserError;
 
-typedef enum {
-  JSON_TOKEN_INVALID = G_TOKEN_LAST,
-
-  JSON_TOKEN_TRUE,
-  JSON_TOKEN_FALSE,
-  JSON_TOKEN_NULL,
-  JSON_TOKEN_VAR,
-
-  JSON_TOKEN_LAST
-} JsonTokenType;
-
 /**
  * JsonParser:
  * 
@@ -129,8 +118,8 @@
   void (* _json_reserved8) (void);
 };
 
-GQuark      json_parser_error_quark    (void);
-GType       json_parser_get_type       (void) G_GNUC_CONST;
+GQuark      json_parser_error_quark      (void);
+GType       json_parser_get_type         (void) G_GNUC_CONST;
 
 JsonParser *json_parser_new              (void);
 gboolean    json_parser_load_from_file   (JsonParser   *parser,
@@ -138,10 +127,9 @@
                                           GError      **error);
 gboolean    json_parser_load_from_data   (JsonParser   *parser,
                                           const gchar  *data,
-                                          gsize         length,
+                                          gssize        length,
                                           GError      **error);
 
-JsonNode *  json_parser_peek_root        (JsonParser   *parser);
 JsonNode *  json_parser_get_root         (JsonParser   *parser);
 
 guint       json_parser_get_current_line (JsonParser   *parser);

Added: trunk/json-glib/json-scanner.c
==============================================================================
--- (empty file)
+++ trunk/json-glib/json-scanner.c	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,1846 @@
+/* json-scanner.c: Tokenizer for JSON
+ * Copyright (C) 2008 OpenedHand
+ *
+ * Based on JsonScanner: Flexible lexical scanner for general purpose.
+ * Copyright (C) 1997, 1998 Tim Janik
+ *
+ * Modified by Emmanuele Bassi <ebassi openedhand com>
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "json-scanner.h"
+
+#ifdef G_OS_WIN32
+#include <io.h> /* For _read() */
+#endif
+
+struct _JsonScannerConfig
+{
+  /* Character sets
+   */
+  gchar *cset_skip_characters; /* default: " \t\n" */
+  gchar *cset_identifier_first;
+  gchar *cset_identifier_nth;
+  gchar *cpair_comment_single; /* default: "#\n" */
+  
+  /* Should symbol lookup work case sensitive? */
+  guint case_sensitive : 1;
+  
+  /* Boolean values to be adjusted "on the fly"
+   * to configure scanning behaviour.
+   */
+  guint skip_comment_multi : 1;  /* C like comment */
+  guint skip_comment_single : 1; /* single line comment */
+  guint scan_comment_multi : 1;  /* scan multi line comments? */
+  guint scan_identifier : 1;
+  guint scan_identifier_1char : 1;
+  guint scan_identifier_NULL : 1;
+  guint scan_symbols : 1;
+  guint scan_binary : 1;
+  guint scan_octal : 1;
+  guint scan_float : 1;
+  guint scan_hex : 1;            /* `0x0ff0' */
+  guint scan_hex_dollar : 1;     /* `$0ff0' */
+  guint scan_string_sq : 1;      /* string: 'anything' */
+  guint scan_string_dq : 1;      /* string: "\\-escapes!\n" */
+  guint numbers_2_int : 1;       /* bin, octal, hex => int */
+  guint int_2_float : 1;         /* int => G_TOKEN_FLOAT? */
+  guint identifier_2_string : 1;
+  guint char_2_token : 1;        /* return G_TOKEN_CHAR? */
+  guint symbol_2_token : 1;
+  guint scope_0_fallback : 1;    /* try scope 0 on lookups? */
+  guint store_int64 : 1;         /* use value.v_int64 rather than v_int */
+  guint padding_dummy;
+};
+
+static JsonScannerConfig json_scanner_config_template =
+{
+  ( " \t\r\n" )		/* cset_skip_characters */,
+  (
+   "_"
+   G_CSET_a_2_z
+   G_CSET_A_2_Z
+  )			/* cset_identifier_first */,
+  (
+   G_CSET_DIGITS
+   "-_"
+   G_CSET_a_2_z
+   G_CSET_A_2_Z
+  )			/* cset_identifier_nth */,
+  ( "//\n" )		/* cpair_comment_single */,
+  TRUE			/* case_sensitive */,
+  TRUE			/* skip_comment_multi */,
+  TRUE			/* skip_comment_single */,
+  FALSE			/* scan_comment_multi */,
+  TRUE			/* scan_identifier */,
+  TRUE			/* scan_identifier_1char */,
+  FALSE			/* scan_identifier_NULL */,
+  TRUE			/* scan_symbols */,
+  TRUE			/* scan_binary */,
+  TRUE			/* scan_octal */,
+  TRUE			/* scan_float */,
+  TRUE			/* scan_hex */,
+  TRUE			/* scan_hex_dollar */,
+  TRUE			/* scan_string_sq */,
+  TRUE			/* scan_string_dq */,
+  TRUE			/* numbers_2_int */,
+  FALSE			/* int_2_float */,
+  FALSE			/* identifier_2_string */,
+  TRUE			/* char_2_token */,
+  TRUE			/* symbol_2_token */,
+  FALSE			/* scope_0_fallback */,
+};
+
+/* --- defines --- */
+#define	to_lower(c)				( \
+	(guchar) (							\
+	  ( (((guchar)(c))>='A' && ((guchar)(c))<='Z') * ('a'-'A') ) |	\
+	  ( (((guchar)(c))>=192 && ((guchar)(c))<=214) * (224-192) ) |	\
+	  ( (((guchar)(c))>=216 && ((guchar)(c))<=222) * (248-216) ) |	\
+	  ((guchar)(c))							\
+	)								\
+)
+
+#define	READ_BUFFER_SIZE	(4000)
+
+static const gchar json_symbol_names[] =
+  "true\0"
+  "false\0"
+  "null\0"
+  "var\0";
+
+static const struct
+{
+  guint name_offset;
+  guint token;
+} json_symbols[] = {
+  {  0, JSON_TOKEN_TRUE },
+  {  5, JSON_TOKEN_FALSE },
+  { 11, JSON_TOKEN_NULL },
+  { 16, JSON_TOKEN_VAR }
+};
+
+static const guint n_json_symbols = G_N_ELEMENTS (json_symbols);
+
+/* --- typedefs --- */
+typedef	struct	_JsonScannerKey JsonScannerKey;
+
+struct	_JsonScannerKey
+{
+  guint scope_id;
+  gchar *symbol;
+  gpointer value;
+};
+
+/* --- prototypes --- */
+static gboolean	json_scanner_key_equal (gconstpointer v1,
+                                        gconstpointer v2);
+static guint    json_scanner_key_hash  (gconstpointer v);
+
+static inline
+JsonScannerKey *json_scanner_lookup_internal (JsonScanner *scanner,
+                                              guint        scope_id,
+                                              const gchar *symbol);
+static void     json_scanner_get_token_ll    (JsonScanner *scanner,
+                                              GTokenType  *token_p,
+                                              GTokenValue *value_p,
+                                              guint       *line_p,
+                                              guint       *position_p);
+static void	json_scanner_get_token_i     (JsonScanner *scanner,
+                                              GTokenType  *token_p,
+                                              GTokenValue *value_p,
+                                              guint       *line_p,
+                                              guint       *position_p);
+
+static guchar   json_scanner_peek_next_char  (JsonScanner *scanner);
+static guchar   json_scanner_get_char        (JsonScanner *scanner,
+                                              guint       *line_p,
+                                              guint       *position_p);
+static gunichar json_scanner_get_unichar     (JsonScanner *scanner,
+                                              guint       *line_p,
+                                              guint       *position_p);
+static void     json_scanner_msg_handler     (JsonScanner *scanner,
+                                              gchar       *message,
+                                              gboolean     is_error);
+
+/* --- functions --- */
+static inline gint
+json_scanner_char_2_num (guchar c,
+                         guchar base)
+{
+  if (c >= '0' && c <= '9')
+    c -= '0';
+  else if (c >= 'A' && c <= 'Z')
+    c -= 'A' - 10;
+  else if (c >= 'a' && c <= 'z')
+    c -= 'a' - 10;
+  else
+    return -1;
+  
+  if (c < base)
+    return c;
+  
+  return -1;
+}
+
+JsonScanner *
+json_scanner_new (void)
+{
+  JsonScanner *scanner;
+  JsonScannerConfig *config_templ;
+  
+  config_templ = &json_scanner_config_template;
+  
+  scanner = g_new0 (JsonScanner, 1);
+  
+  scanner->user_data = NULL;
+  scanner->max_parse_errors = 1;
+  scanner->parse_errors	= 0;
+  scanner->input_name = NULL;
+  g_datalist_init (&scanner->qdata);
+  
+  scanner->config = g_new0 (JsonScannerConfig, 1);
+  
+  scanner->config->case_sensitive	 = config_templ->case_sensitive;
+  scanner->config->cset_skip_characters	 = config_templ->cset_skip_characters;
+  if (!scanner->config->cset_skip_characters)
+    scanner->config->cset_skip_characters = "";
+  scanner->config->cset_identifier_first = config_templ->cset_identifier_first;
+  scanner->config->cset_identifier_nth	 = config_templ->cset_identifier_nth;
+  scanner->config->cpair_comment_single	 = config_templ->cpair_comment_single;
+  scanner->config->skip_comment_multi	 = config_templ->skip_comment_multi;
+  scanner->config->skip_comment_single	 = config_templ->skip_comment_single;
+  scanner->config->scan_comment_multi	 = config_templ->scan_comment_multi;
+  scanner->config->scan_identifier	 = config_templ->scan_identifier;
+  scanner->config->scan_identifier_1char = config_templ->scan_identifier_1char;
+  scanner->config->scan_identifier_NULL	 = config_templ->scan_identifier_NULL;
+  scanner->config->scan_symbols		 = config_templ->scan_symbols;
+  scanner->config->scan_binary		 = config_templ->scan_binary;
+  scanner->config->scan_octal		 = config_templ->scan_octal;
+  scanner->config->scan_float		 = config_templ->scan_float;
+  scanner->config->scan_hex		 = config_templ->scan_hex;
+  scanner->config->scan_hex_dollar	 = config_templ->scan_hex_dollar;
+  scanner->config->scan_string_sq	 = config_templ->scan_string_sq;
+  scanner->config->scan_string_dq	 = config_templ->scan_string_dq;
+  scanner->config->numbers_2_int	 = config_templ->numbers_2_int;
+  scanner->config->int_2_float		 = config_templ->int_2_float;
+  scanner->config->identifier_2_string	 = config_templ->identifier_2_string;
+  scanner->config->char_2_token		 = config_templ->char_2_token;
+  scanner->config->symbol_2_token	 = config_templ->symbol_2_token;
+  scanner->config->scope_0_fallback	 = config_templ->scope_0_fallback;
+  scanner->config->store_int64		 = config_templ->store_int64;
+  
+  scanner->token = G_TOKEN_NONE;
+  scanner->value.v_int64 = 0;
+  scanner->line = 1;
+  scanner->position = 0;
+  
+  scanner->next_token = G_TOKEN_NONE;
+  scanner->next_value.v_int64 = 0;
+  scanner->next_line = 1;
+  scanner->next_position = 0;
+  
+  scanner->symbol_table = g_hash_table_new (json_scanner_key_hash,
+                                            json_scanner_key_equal);
+  scanner->input_fd = -1;
+  scanner->text = NULL;
+  scanner->text_end = NULL;
+  scanner->buffer = NULL;
+  scanner->scope_id = 0;
+  
+  scanner->msg_handler = json_scanner_msg_handler;
+  
+  return scanner;
+}
+
+static inline void
+json_scanner_free_value (GTokenType  *token_p,
+                         GTokenValue *value_p)
+{
+  switch (*token_p)
+    {
+    case G_TOKEN_STRING:
+    case G_TOKEN_IDENTIFIER:
+    case G_TOKEN_IDENTIFIER_NULL:
+    case G_TOKEN_COMMENT_SINGLE:
+    case G_TOKEN_COMMENT_MULTI:
+      g_free (value_p->v_string);
+      break;
+      
+    default:
+      break;
+    }
+  
+  *token_p = G_TOKEN_NONE;
+}
+
+static void
+json_scanner_destroy_symbol_table_entry (gpointer _key,
+                                         gpointer _value,
+                                         gpointer _data)
+{
+  JsonScannerKey *key = _key;
+  
+  g_free (key->symbol);
+  g_slice_free (JsonScannerKey, key);
+}
+
+void
+json_scanner_destroy (JsonScanner *scanner)
+{
+  g_return_if_fail (scanner != NULL);
+  
+  g_datalist_clear (&scanner->qdata);
+  g_hash_table_foreach (scanner->symbol_table, 
+			json_scanner_destroy_symbol_table_entry,
+                        NULL);
+  g_hash_table_destroy (scanner->symbol_table);
+  json_scanner_free_value (&scanner->token, &scanner->value);
+  json_scanner_free_value (&scanner->next_token, &scanner->next_value);
+  g_free (scanner->config);
+  g_free (scanner->buffer);
+  g_free (scanner);
+}
+
+static void
+json_scanner_msg_handler (JsonScanner *scanner,
+                          gchar       *message,
+                          gboolean     is_error)
+{
+  g_return_if_fail (scanner != NULL);
+  
+  g_fprintf (stderr, "%s:%d: ",
+	     scanner->input_name ? scanner->input_name : "<memory>",
+	     scanner->line);
+  if (is_error)
+    g_fprintf (stderr, "error: ");
+
+  g_fprintf (stderr, "%s\n", message);
+}
+
+void
+json_scanner_error (JsonScanner *scanner,
+                    const gchar *format,
+                    ...)
+{
+  g_return_if_fail (scanner != NULL);
+  g_return_if_fail (format != NULL);
+  
+  scanner->parse_errors++;
+  
+  if (scanner->msg_handler)
+    {
+      va_list args;
+      gchar *string;
+      
+      va_start (args, format);
+      string = g_strdup_vprintf (format, args);
+      va_end (args);
+      
+      scanner->msg_handler (scanner, string, TRUE);
+      
+      g_free (string);
+    }
+}
+
+void
+json_scanner_warn (JsonScanner *scanner,
+                   const gchar *format,
+                   ...)
+{
+  g_return_if_fail (scanner != NULL);
+  g_return_if_fail (format != NULL);
+  
+  if (scanner->msg_handler)
+    {
+      va_list args;
+      gchar *string;
+      
+      va_start (args, format);
+      string = g_strdup_vprintf (format, args);
+      va_end (args);
+      
+      scanner->msg_handler (scanner, string, FALSE);
+      
+      g_free (string);
+    }
+}
+
+static gboolean
+json_scanner_key_equal (gconstpointer v1,
+                        gconstpointer v2)
+{
+  const JsonScannerKey *key1 = v1;
+  const JsonScannerKey *key2 = v2;
+  
+  return (key1->scope_id == key2->scope_id) &&
+         (strcmp (key1->symbol, key2->symbol) == 0);
+}
+
+static guint
+json_scanner_key_hash (gconstpointer v)
+{
+  const JsonScannerKey *key = v;
+  gchar *c;
+  guint h;
+  
+  h = key->scope_id;
+  for (c = key->symbol; *c; c++)
+    h = (h << 5) - h + *c;
+  
+  return h;
+}
+
+static inline JsonScannerKey *
+json_scanner_lookup_internal (JsonScanner *scanner,
+                              guint        scope_id,
+                              const gchar *symbol)
+{
+  JsonScannerKey *key_p;
+  JsonScannerKey key;
+  
+  key.scope_id = scope_id;
+  
+  if (!scanner->config->case_sensitive)
+    {
+      gchar *d;
+      const gchar *c;
+      
+      key.symbol = g_new (gchar, strlen (symbol) + 1);
+      for (d = key.symbol, c = symbol; *c; c++, d++)
+	*d = to_lower (*c);
+      *d = 0;
+      key_p = g_hash_table_lookup (scanner->symbol_table, &key);
+      g_free (key.symbol);
+    }
+  else
+    {
+      key.symbol = (gchar*) symbol;
+      key_p = g_hash_table_lookup (scanner->symbol_table, &key);
+    }
+  
+  return key_p;
+}
+
+void
+json_scanner_scope_add_symbol (JsonScanner *scanner,
+                               guint        scope_id,
+                               const gchar *symbol,
+                               gpointer     value)
+{
+  JsonScannerKey *key;
+
+  g_return_if_fail (scanner != NULL);
+  g_return_if_fail (symbol != NULL);
+
+  key = json_scanner_lookup_internal (scanner, scope_id, symbol);
+  if (!key)
+    {
+      key = g_slice_new (JsonScannerKey);
+      key->scope_id = scope_id;
+      key->symbol = g_strdup (symbol);
+      key->value = value;
+      if (!scanner->config->case_sensitive)
+	{
+	  gchar *c;
+
+	  c = key->symbol;
+	  while (*c != 0)
+	    {
+	      *c = to_lower (*c);
+	      c++;
+	    }
+	}
+
+      g_hash_table_insert (scanner->symbol_table, key, key);
+    }
+  else
+    key->value = value;
+}
+
+void
+json_scanner_scope_remove_symbol (JsonScanner *scanner,
+                                  guint        scope_id,
+                                  const gchar *symbol)
+{
+  JsonScannerKey *key;
+
+  g_return_if_fail (scanner != NULL);
+  g_return_if_fail (symbol != NULL);
+
+  key = json_scanner_lookup_internal (scanner, scope_id, symbol);
+  if (key)
+    {
+      g_hash_table_remove (scanner->symbol_table, key);
+      g_free (key->symbol);
+      g_slice_free (JsonScannerKey, key);
+    }
+}
+
+gpointer
+json_scanner_lookup_symbol (JsonScanner *scanner,
+                            const gchar *symbol)
+{
+  JsonScannerKey *key;
+  guint scope_id;
+
+  g_return_val_if_fail (scanner != NULL, NULL);
+
+  if (!symbol)
+    return NULL;
+
+  scope_id = scanner->scope_id;
+  key = json_scanner_lookup_internal (scanner, scope_id, symbol);
+  if (!key && scope_id && scanner->config->scope_0_fallback)
+    key = json_scanner_lookup_internal (scanner, 0, symbol);
+
+  if (key)
+    return key->value;
+  else
+    return NULL;
+}
+
+gpointer
+json_scanner_scope_lookup_symbol (JsonScanner *scanner,
+                                  guint        scope_id,
+                                  const gchar *symbol)
+{
+  JsonScannerKey *key;
+
+  g_return_val_if_fail (scanner != NULL, NULL);
+
+  if (!symbol)
+    return NULL;
+
+  key = json_scanner_lookup_internal (scanner, scope_id, symbol);
+
+  if (key)
+    return key->value;
+  else
+    return NULL;
+}
+
+guint
+json_scanner_set_scope (JsonScanner *scanner,
+                        guint        scope_id)
+{
+  guint old_scope_id;
+
+  g_return_val_if_fail (scanner != NULL, 0);
+
+  old_scope_id = scanner->scope_id;
+  scanner->scope_id = scope_id;
+
+  return old_scope_id;
+}
+
+typedef struct {
+  GHFunc func;
+  gpointer data;
+  guint scope_id;
+} ForeachClosure;
+
+static void
+json_scanner_foreach_internal (gpointer _key,
+                               gpointer _value,
+                               gpointer _user_data)
+{
+  JsonScannerKey *key = _value;
+  ForeachClosure *closure = _user_data;
+
+  if (key->scope_id == closure->scope_id)
+    closure->func (key->symbol, key->value, closure->data);
+}
+
+void
+json_scanner_scope_foreach_symbol (JsonScanner *scanner,
+                                   guint        scope_id,
+                                   GHFunc       func,
+                                   gpointer     user_data)
+{
+  ForeachClosure closure;
+
+  g_return_if_fail (scanner != NULL);
+  g_return_if_fail (func != NULL);
+
+  closure.func = func;
+  closure.data = user_data;
+  closure.scope_id = scope_id;
+
+  g_hash_table_foreach (scanner->symbol_table,
+                        json_scanner_foreach_internal,
+                        &closure);
+}
+
+GTokenType
+json_scanner_peek_next_token (JsonScanner *scanner)
+{
+  g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
+
+  if (scanner->next_token == G_TOKEN_NONE)
+    {
+      scanner->next_line = scanner->line;
+      scanner->next_position = scanner->position;
+      json_scanner_get_token_i (scanner,
+                                &scanner->next_token,
+                                &scanner->next_value,
+                                &scanner->next_line,
+                                &scanner->next_position);
+    }
+
+  return scanner->next_token;
+}
+
+GTokenType
+json_scanner_get_next_token (JsonScanner *scanner)
+{
+  g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
+
+  if (scanner->next_token != G_TOKEN_NONE)
+    {
+      json_scanner_free_value (&scanner->token, &scanner->value);
+
+      scanner->token = scanner->next_token;
+      scanner->value = scanner->next_value;
+      scanner->line = scanner->next_line;
+      scanner->position = scanner->next_position;
+      scanner->next_token = G_TOKEN_NONE;
+    }
+  else
+    json_scanner_get_token_i (scanner,
+                              &scanner->token,
+                              &scanner->value,
+                              &scanner->line,
+                              &scanner->position);
+
+  return scanner->token;
+}
+
+GTokenType
+json_scanner_cur_token (JsonScanner *scanner)
+{
+  g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
+
+  return scanner->token;
+}
+
+GTokenValue
+json_scanner_cur_value (JsonScanner *scanner)
+{
+  GTokenValue v;
+
+  v.v_int64 = 0;
+
+  g_return_val_if_fail (scanner != NULL, v);
+
+  /* MSC isn't capable of handling return scanner->value; ? */
+
+  v = scanner->value;
+
+  return v;
+}
+
+guint
+json_scanner_cur_line (JsonScanner *scanner)
+{
+  g_return_val_if_fail (scanner != NULL, 0);
+
+  return scanner->line;
+}
+
+guint
+json_scanner_cur_position (JsonScanner *scanner)
+{
+  g_return_val_if_fail (scanner != NULL, 0);
+
+  return scanner->position;
+}
+
+gboolean
+json_scanner_eof (JsonScanner *scanner)
+{
+  g_return_val_if_fail (scanner != NULL, TRUE);
+
+  return scanner->token == G_TOKEN_EOF || scanner->token == G_TOKEN_ERROR;
+}
+
+void
+json_scanner_input_file (JsonScanner *scanner,
+                         gint         input_fd)
+{
+  g_return_if_fail (scanner != NULL);
+  g_return_if_fail (input_fd >= 0);
+
+  if (scanner->input_fd >= 0)
+    json_scanner_sync_file_offset (scanner);
+
+  scanner->token = G_TOKEN_NONE;
+  scanner->value.v_int64 = 0;
+  scanner->line = 1;
+  scanner->position = 0;
+  scanner->next_token = G_TOKEN_NONE;
+
+  scanner->input_fd = input_fd;
+  scanner->text = NULL;
+  scanner->text_end = NULL;
+
+  if (!scanner->buffer)
+    scanner->buffer = g_new (gchar, READ_BUFFER_SIZE + 1);
+}
+
+void
+json_scanner_input_text (JsonScanner *scanner,
+                         const gchar *text,
+                         guint        text_len)
+{
+  g_return_if_fail (scanner != NULL);
+  if (text_len)
+    g_return_if_fail (text != NULL);
+  else
+    text = NULL;
+
+  if (scanner->input_fd >= 0)
+    json_scanner_sync_file_offset (scanner);
+
+  scanner->token = G_TOKEN_NONE;
+  scanner->value.v_int64 = 0;
+  scanner->line = 1;
+  scanner->position = 0;
+  scanner->next_token = G_TOKEN_NONE;
+
+  scanner->input_fd = -1;
+  scanner->text = text;
+  scanner->text_end = text + text_len;
+
+  if (scanner->buffer)
+    {
+      g_free (scanner->buffer);
+      scanner->buffer = NULL;
+    }
+}
+
+static guchar
+json_scanner_peek_next_char (JsonScanner *scanner)
+{
+  if (scanner->text < scanner->text_end)
+    return *scanner->text;
+  else if (scanner->input_fd >= 0)
+    {
+      gint count;
+      gchar *buffer;
+
+      buffer = scanner->buffer;
+      do
+	{
+	  count = read (scanner->input_fd, buffer, READ_BUFFER_SIZE);
+	}
+      while (count == -1 && (errno == EINTR || errno == EAGAIN));
+
+      if (count < 1)
+	{
+	  scanner->input_fd = -1;
+
+	  return 0;
+	}
+      else
+	{
+	  scanner->text = buffer;
+	  scanner->text_end = buffer + count;
+
+	  return *buffer;
+	}
+    }
+  else
+    return 0;
+}
+
+void
+json_scanner_sync_file_offset (JsonScanner *scanner)
+{
+  g_return_if_fail (scanner != NULL);
+
+  /* for file input, rewind the filedescriptor to the current
+   * buffer position and blow the file read ahead buffer. useful
+   * for third party uses of our file descriptor, which hooks 
+   * onto the current scanning position.
+   */
+
+  if (scanner->input_fd >= 0 && scanner->text_end > scanner->text)
+    {
+      gint buffered;
+
+      buffered = scanner->text_end - scanner->text;
+      if (lseek (scanner->input_fd, - buffered, SEEK_CUR) >= 0)
+	{
+	  /* we succeeded, blow our buffer's contents now */
+	  scanner->text = NULL;
+	  scanner->text_end = NULL;
+	}
+      else
+	errno = 0;
+    }
+}
+
+static guchar
+json_scanner_get_char (JsonScanner *scanner,
+                       guint       *line_p,
+                       guint       *position_p)
+{
+  guchar fchar;
+
+  if (scanner->text < scanner->text_end)
+    fchar = *(scanner->text++);
+  else if (scanner->input_fd >= 0)
+    {
+      gint count;
+      gchar *buffer;
+
+      buffer = scanner->buffer;
+      do
+	{
+	  count = read (scanner->input_fd, buffer, READ_BUFFER_SIZE);
+	}
+      while (count == -1 && (errno == EINTR || errno == EAGAIN));
+
+      if (count < 1)
+	{
+	  scanner->input_fd = -1;
+	  fchar = 0;
+	}
+      else
+	{
+	  scanner->text = buffer + 1;
+	  scanner->text_end = buffer + count;
+	  fchar = *buffer;
+	  if (!fchar)
+	    {
+	      json_scanner_sync_file_offset (scanner);
+	      scanner->text_end = scanner->text;
+	      scanner->input_fd = -1;
+	    }
+	}
+    }
+  else
+    fchar = 0;
+  
+  if (fchar == '\n')
+    {
+      (*position_p) = 0;
+      (*line_p)++;
+    }
+  else if (fchar)
+    {
+      (*position_p)++;
+    }
+  
+  return fchar;
+}
+
+#define is_hex_digit(c)         (((c) >= '0' && (c) <= '9') || \
+                                 ((c) >= 'a' && (c) <= 'f') || \
+                                 ((c) >= 'A' && (c) <= 'F'))
+#define to_hex_digit(c)         (((c) <= '9') ? (c) - '0' : ((c) & 7) + 9)
+
+static gunichar
+json_scanner_get_unichar (JsonScanner *scanner,
+                          guint       *line_p,
+                          guint       *position_p)
+{
+  gunichar uchar;
+  gchar ch;
+  gint i;
+
+  uchar = 0;
+  for (i = 0; i < 4; i++)
+    {
+      ch = json_scanner_get_char (scanner, line_p, position_p);
+
+      if (is_hex_digit (ch))
+        uchar += ((gunichar) to_hex_digit (ch) << ((3 - i) * 4));
+      else
+        break;
+    }
+
+  g_assert (g_unichar_validate (uchar));
+
+  return uchar;
+}
+
+void
+json_scanner_unexp_token (JsonScanner *scanner,
+                          GTokenType   expected_token,
+                          const gchar *identifier_spec,
+                          const gchar *symbol_spec,
+                          const gchar *symbol_name,
+                          const gchar *message,
+                          gint         is_error)
+{
+  gchar	*token_string;
+  guint	token_string_len;
+  gchar	*expected_string;
+  guint	expected_string_len;
+  gchar	*message_prefix;
+  gboolean print_unexp;
+  void (*msg_handler) (JsonScanner*, const gchar*, ...);
+  
+  g_return_if_fail (scanner != NULL);
+  
+  if (is_error)
+    msg_handler = json_scanner_error;
+  else
+    msg_handler = json_scanner_warn;
+  
+  if (!identifier_spec)
+    identifier_spec = "identifier";
+  if (!symbol_spec)
+    symbol_spec = "symbol";
+  
+  token_string_len = 56;
+  token_string = g_new (gchar, token_string_len + 1);
+  expected_string_len = 64;
+  expected_string = g_new (gchar, expected_string_len + 1);
+  print_unexp = TRUE;
+  
+  switch (scanner->token)
+    {
+    case G_TOKEN_EOF:
+      g_snprintf (token_string, token_string_len, "end of file");
+      break;
+      
+    default:
+      if (scanner->token >= 1 && scanner->token <= 255)
+	{
+	  if ((scanner->token >= ' ' && scanner->token <= '~') ||
+	      strchr (scanner->config->cset_identifier_first, scanner->token) ||
+	      strchr (scanner->config->cset_identifier_nth, scanner->token))
+	    g_snprintf (token_string, token_string_len, "character `%c'", scanner->token);
+	  else
+	    g_snprintf (token_string, token_string_len, "character `\\%o'", scanner->token);
+	  break;
+	}
+      else if (!scanner->config->symbol_2_token)
+	{
+	  g_snprintf (token_string, token_string_len, "(unknown) token <%d>", scanner->token);
+	  break;
+	}
+      /* fall through */
+    case G_TOKEN_SYMBOL:
+      if (expected_token == G_TOKEN_SYMBOL ||
+	  (scanner->config->symbol_2_token &&
+	   expected_token > G_TOKEN_LAST))
+	print_unexp = FALSE;
+      if (symbol_name)
+	g_snprintf (token_string, token_string_len,
+                    "%s%s `%s'",
+                    print_unexp ? "" : "invalid ",
+                    symbol_spec,
+                    symbol_name);
+      else
+	g_snprintf (token_string, token_string_len,
+                    "%s%s",
+                    print_unexp ? "" : "invalid ",
+                    symbol_spec);
+      break;
+ 
+    case G_TOKEN_ERROR:
+      print_unexp = FALSE;
+      expected_token = G_TOKEN_NONE;
+      switch (scanner->value.v_error)
+	{
+	case G_ERR_UNEXP_EOF:
+	  g_snprintf (token_string, token_string_len, "scanner: unexpected end of file");
+	  break;
+	  
+	case G_ERR_UNEXP_EOF_IN_STRING:
+	  g_snprintf (token_string, token_string_len, "scanner: unterminated string constant");
+	  break;
+	  
+	case G_ERR_UNEXP_EOF_IN_COMMENT:
+	  g_snprintf (token_string, token_string_len, "scanner: unterminated comment");
+	  break;
+	  
+	case G_ERR_NON_DIGIT_IN_CONST:
+	  g_snprintf (token_string, token_string_len, "scanner: non digit in constant");
+	  break;
+	  
+	case G_ERR_FLOAT_RADIX:
+	  g_snprintf (token_string, token_string_len, "scanner: invalid radix for floating constant");
+	  break;
+	  
+	case G_ERR_FLOAT_MALFORMED:
+	  g_snprintf (token_string, token_string_len, "scanner: malformed floating constant");
+	  break;
+	  
+	case G_ERR_DIGIT_RADIX:
+	  g_snprintf (token_string, token_string_len, "scanner: digit is beyond radix");
+	  break;
+	  
+	case G_ERR_UNKNOWN:
+	default:
+	  g_snprintf (token_string, token_string_len, "scanner: unknown error");
+	  break;
+	}
+      break;
+      
+    case G_TOKEN_CHAR:
+      g_snprintf (token_string, token_string_len, "character `%c'", scanner->value.v_char);
+      break;
+      
+    case G_TOKEN_IDENTIFIER:
+    case G_TOKEN_IDENTIFIER_NULL:
+      if (expected_token == G_TOKEN_IDENTIFIER ||
+	  expected_token == G_TOKEN_IDENTIFIER_NULL)
+	print_unexp = FALSE;
+      g_snprintf (token_string, token_string_len,
+                  "%s%s `%s'",
+                  print_unexp ? "" : "invalid ",
+                  identifier_spec,
+                  scanner->token == G_TOKEN_IDENTIFIER ? scanner->value.v_string : "null");
+      break;
+      
+    case G_TOKEN_BINARY:
+    case G_TOKEN_OCTAL:
+    case G_TOKEN_INT:
+    case G_TOKEN_HEX:
+      if (scanner->config->store_int64)
+	g_snprintf (token_string, token_string_len, "number `%" G_GUINT64_FORMAT "'", scanner->value.v_int64);
+      else
+	g_snprintf (token_string, token_string_len, "number `%lu'", scanner->value.v_int);
+      break;
+      
+    case G_TOKEN_FLOAT:
+      g_snprintf (token_string, token_string_len, "number `%.3f'", scanner->value.v_float);
+      break;
+      
+    case G_TOKEN_STRING:
+      if (expected_token == G_TOKEN_STRING)
+	print_unexp = FALSE;
+      g_snprintf (token_string, token_string_len,
+                  "%s%sstring constant \"%s\"",
+                  print_unexp ? "" : "invalid ",
+                  scanner->value.v_string[0] == 0 ? "empty " : "",
+                  scanner->value.v_string);
+      token_string[token_string_len - 2] = '"';
+      token_string[token_string_len - 1] = 0;
+      break;
+      
+    case G_TOKEN_COMMENT_SINGLE:
+    case G_TOKEN_COMMENT_MULTI:
+      g_snprintf (token_string, token_string_len, "comment");
+      break;
+      
+    case G_TOKEN_NONE:
+      /* somehow the user's parsing code is screwed, there isn't much
+       * we can do about it.
+       * Note, a common case to trigger this is
+       * json_scanner_peek_next_token(); json_scanner_unexp_token();
+       * without an intermediate json_scanner_get_next_token().
+       */
+      g_assert_not_reached ();
+      break;
+    }
+  
+  
+  switch (expected_token)
+    {
+      gboolean need_valid;
+      gchar *tstring;
+    case G_TOKEN_EOF:
+      g_snprintf (expected_string, expected_string_len, "end of file");
+      break;
+    default:
+      if (expected_token >= 1 && expected_token <= 255)
+	{
+	  if ((expected_token >= ' ' && expected_token <= '~') ||
+	      strchr (scanner->config->cset_identifier_first, expected_token) ||
+	      strchr (scanner->config->cset_identifier_nth, expected_token))
+	    g_snprintf (expected_string, expected_string_len, "character `%c'", expected_token);
+	  else
+	    g_snprintf (expected_string, expected_string_len, "character `\\%o'", expected_token);
+	  break;
+	}
+      else if (!scanner->config->symbol_2_token)
+	{
+	  g_snprintf (expected_string, expected_string_len, "(unknown) token <%d>", expected_token);
+	  break;
+	}
+      /* fall through */
+    case G_TOKEN_SYMBOL:
+      need_valid = (scanner->token == G_TOKEN_SYMBOL ||
+		    (scanner->config->symbol_2_token &&
+		     scanner->token > G_TOKEN_LAST));
+      g_snprintf (expected_string, expected_string_len,
+                  "%s%s",
+                  need_valid ? "valid " : "",
+                  symbol_spec);
+      /* FIXME: should we attempt to lookup the symbol_name for symbol_2_token? */
+      break;
+    case G_TOKEN_CHAR:
+      g_snprintf (expected_string, expected_string_len, "%scharacter",
+		  scanner->token == G_TOKEN_CHAR ? "valid " : "");
+      break;
+    case G_TOKEN_BINARY:
+      tstring = "binary";
+      g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
+		  scanner->token == expected_token ? "valid " : "", tstring);
+      break;
+    case G_TOKEN_OCTAL:
+      tstring = "octal";
+      g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
+		  scanner->token == expected_token ? "valid " : "", tstring);
+      break;
+    case G_TOKEN_INT:
+      tstring = "integer";
+      g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
+		  scanner->token == expected_token ? "valid " : "", tstring);
+      break;
+    case G_TOKEN_HEX:
+      tstring = "hexadecimal";
+      g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
+		  scanner->token == expected_token ? "valid " : "", tstring);
+      break;
+    case G_TOKEN_FLOAT:
+      tstring = "float";
+      g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
+		  scanner->token == expected_token ? "valid " : "", tstring);
+      break;
+    case G_TOKEN_STRING:
+      g_snprintf (expected_string,
+		  expected_string_len,
+		  "%sstring constant",
+		  scanner->token == G_TOKEN_STRING ? "valid " : "");
+      break;
+    case G_TOKEN_IDENTIFIER:
+    case G_TOKEN_IDENTIFIER_NULL:
+      need_valid = (scanner->token == G_TOKEN_IDENTIFIER_NULL ||
+		    scanner->token == G_TOKEN_IDENTIFIER);
+      g_snprintf (expected_string,
+		  expected_string_len,
+		  "%s%s",
+		  need_valid ? "valid " : "",
+		  identifier_spec);
+      break;
+    case G_TOKEN_COMMENT_SINGLE:
+      tstring = "single-line";
+      g_snprintf (expected_string, expected_string_len, "%scomment (%s)",
+		  scanner->token == expected_token ? "valid " : "", tstring);
+      break;
+    case G_TOKEN_COMMENT_MULTI:
+      tstring = "multi-line";
+      g_snprintf (expected_string, expected_string_len, "%scomment (%s)",
+		  scanner->token == expected_token ? "valid " : "", tstring);
+      break;
+    case G_TOKEN_NONE:
+    case G_TOKEN_ERROR:
+      /* this is handled upon printout */
+      break;
+    }
+  
+  if (message && message[0] != 0)
+    message_prefix = " - ";
+  else
+    {
+      message_prefix = "";
+      message = "";
+    }
+  if (expected_token == G_TOKEN_ERROR)
+    {
+      msg_handler (scanner,
+		   "failure around %s%s%s",
+		   token_string,
+		   message_prefix,
+		   message);
+    }
+  else if (expected_token == G_TOKEN_NONE)
+    {
+      if (print_unexp)
+	msg_handler (scanner,
+		     "unexpected %s%s%s",
+		     token_string,
+		     message_prefix,
+		     message);
+      else
+	msg_handler (scanner,
+		     "%s%s%s",
+		     token_string,
+		     message_prefix,
+		     message);
+    }
+  else
+    {
+      if (print_unexp)
+	msg_handler (scanner,
+		     "unexpected %s, expected %s%s%s",
+		     token_string,
+		     expected_string,
+		     message_prefix,
+		     message);
+      else
+	msg_handler (scanner,
+		     "%s, expected %s%s%s",
+		     token_string,
+		     expected_string,
+		     message_prefix,
+		     message);
+    }
+  
+  g_free (token_string);
+  g_free (expected_string);
+}
+
+static void
+json_scanner_get_token_i (JsonScanner	*scanner,
+		       GTokenType	*token_p,
+		       GTokenValue	*value_p,
+		       guint		*line_p,
+		       guint		*position_p)
+{
+  do
+    {
+      json_scanner_free_value (token_p, value_p);
+      json_scanner_get_token_ll (scanner, token_p, value_p, line_p, position_p);
+    }
+  while (((*token_p > 0 && *token_p < 256) &&
+	  strchr (scanner->config->cset_skip_characters, *token_p)) ||
+	 (*token_p == G_TOKEN_CHAR &&
+	  strchr (scanner->config->cset_skip_characters, value_p->v_char)) ||
+	 (*token_p == G_TOKEN_COMMENT_MULTI &&
+	  scanner->config->skip_comment_multi) ||
+	 (*token_p == G_TOKEN_COMMENT_SINGLE &&
+	  scanner->config->skip_comment_single));
+  
+  switch (*token_p)
+    {
+    case G_TOKEN_IDENTIFIER:
+      if (scanner->config->identifier_2_string)
+	*token_p = G_TOKEN_STRING;
+      break;
+      
+    case G_TOKEN_SYMBOL:
+      if (scanner->config->symbol_2_token)
+	*token_p = (GTokenType) value_p->v_symbol;
+      break;
+      
+    case G_TOKEN_BINARY:
+    case G_TOKEN_OCTAL:
+    case G_TOKEN_HEX:
+      if (scanner->config->numbers_2_int)
+	*token_p = G_TOKEN_INT;
+      break;
+      
+    default:
+      break;
+    }
+  
+  if (*token_p == G_TOKEN_INT &&
+      scanner->config->int_2_float)
+    {
+      *token_p = G_TOKEN_FLOAT;
+      if (scanner->config->store_int64)
+        {
+#ifdef _MSC_VER
+          /* work around error C2520, see gvaluetransform.c */
+          value_p->v_float = (__int64)value_p->v_int64;
+#else
+          value_p->v_float = value_p->v_int64;
+#endif
+        }
+      else
+	value_p->v_float = value_p->v_int;
+    }
+  
+  errno = 0;
+}
+
+static void
+json_scanner_get_token_ll (JsonScanner *scanner,
+                           GTokenType  *token_p,
+                           GTokenValue *value_p,
+                           guint       *line_p,
+                           guint       *position_p)
+{
+  JsonScannerConfig *config;
+  GTokenType	   token;
+  gboolean	   in_comment_multi;
+  gboolean	   in_comment_single;
+  gboolean	   in_string_sq;
+  gboolean	   in_string_dq;
+  GString	  *gstring;
+  GTokenValue	   value;
+  guchar	   ch;
+  
+  config = scanner->config;
+  (*value_p).v_int64 = 0;
+  
+  if ((scanner->text >= scanner->text_end && scanner->input_fd < 0) ||
+      scanner->token == G_TOKEN_EOF)
+    {
+      *token_p = G_TOKEN_EOF;
+      return;
+    }
+  
+  in_comment_multi = FALSE;
+  in_comment_single = FALSE;
+  in_string_sq = FALSE;
+  in_string_dq = FALSE;
+  gstring = NULL;
+  
+  do /* while (ch != 0) */
+    {
+      gboolean dotted_float = FALSE;
+      
+      ch = json_scanner_get_char (scanner, line_p, position_p);
+      
+      value.v_int64 = 0;
+      token = G_TOKEN_NONE;
+      
+      /* this is *evil*, but needed ;(
+       * we first check for identifier first character, because	 it
+       * might interfere with other key chars like slashes or numbers
+       */
+      if (config->scan_identifier &&
+	  ch && strchr (config->cset_identifier_first, ch))
+	goto identifier_precedence;
+      
+      switch (ch)
+	{
+	case 0:
+	  token = G_TOKEN_EOF;
+	  (*position_p)++;
+	  /* ch = 0; */
+	  break;
+	  
+	case '/':
+	  if (!config->scan_comment_multi ||
+	      json_scanner_peek_next_char (scanner) != '*')
+	    goto default_case;
+	  json_scanner_get_char (scanner, line_p, position_p);
+	  token = G_TOKEN_COMMENT_MULTI;
+	  in_comment_multi = TRUE;
+	  gstring = g_string_new (NULL);
+	  while ((ch = json_scanner_get_char (scanner, line_p, position_p)) != 0)
+	    {
+	      if (ch == '*' && json_scanner_peek_next_char (scanner) == '/')
+		{
+		  json_scanner_get_char (scanner, line_p, position_p);
+		  in_comment_multi = FALSE;
+		  break;
+		}
+	      else
+		gstring = g_string_append_c (gstring, ch);
+	    }
+	  ch = 0;
+	  break;
+	  
+	case '\'':
+	  if (!config->scan_string_sq)
+	    goto default_case;
+	  token = G_TOKEN_STRING;
+	  in_string_sq = TRUE;
+	  gstring = g_string_new (NULL);
+	  while ((ch = json_scanner_get_char (scanner, line_p, position_p)) != 0)
+	    {
+	      if (ch == '\'')
+		{
+		  in_string_sq = FALSE;
+		  break;
+		}
+	      else
+		gstring = g_string_append_c (gstring, ch);
+	    }
+	  ch = 0;
+	  break;
+	  
+	case '"':
+	  if (!config->scan_string_dq)
+	    goto default_case;
+	  token = G_TOKEN_STRING;
+	  in_string_dq = TRUE;
+	  gstring = g_string_new (NULL);
+	  while ((ch = json_scanner_get_char (scanner, line_p, position_p)) != 0)
+	    {
+	      if (ch == '"')
+		{
+		  in_string_dq = FALSE;
+		  break;
+		}
+	      else
+		{
+		  if (ch == '\\')
+		    {
+		      ch = json_scanner_get_char (scanner, line_p, position_p);
+		      switch (ch)
+			{
+			  guint	i;
+			  guint	fchar;
+			  
+			case 0:
+			  break;
+			  
+			case '\\':
+			  gstring = g_string_append_c (gstring, '\\');
+			  break;
+			  
+			case 'n':
+			  gstring = g_string_append_c (gstring, '\n');
+			  break;
+			  
+			case 't':
+			  gstring = g_string_append_c (gstring, '\t');
+			  break;
+			  
+			case 'r':
+			  gstring = g_string_append_c (gstring, '\r');
+			  break;
+			  
+			case 'b':
+			  gstring = g_string_append_c (gstring, '\b');
+			  break;
+			  
+			case 'f':
+			  gstring = g_string_append_c (gstring, '\f');
+			  break;
+
+                        case 'u':
+                          fchar = json_scanner_peek_next_char (scanner);
+                          if (is_hex_digit (fchar))
+                            {
+                              gunichar ucs;
+
+                              ucs = json_scanner_get_unichar (scanner, line_p, position_p);
+                              gstring = g_string_append_unichar (gstring, ucs);
+                            }
+                          break;
+			  
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			  i = ch - '0';
+			  fchar = json_scanner_peek_next_char (scanner);
+			  if (fchar >= '0' && fchar <= '7')
+			    {
+			      ch = json_scanner_get_char (scanner, line_p, position_p);
+			      i = i * 8 + ch - '0';
+			      fchar = json_scanner_peek_next_char (scanner);
+			      if (fchar >= '0' && fchar <= '7')
+				{
+				  ch = json_scanner_get_char (scanner, line_p, position_p);
+				  i = i * 8 + ch - '0';
+				}
+			    }
+			  gstring = g_string_append_c (gstring, i);
+			  break;
+			  
+			default:
+			  gstring = g_string_append_c (gstring, ch);
+			  break;
+			}
+		    }
+		  else
+		    gstring = g_string_append_c (gstring, ch);
+		}
+	    }
+	  ch = 0;
+	  break;
+	  
+	case '.':
+	  if (!config->scan_float)
+	    goto default_case;
+	  token = G_TOKEN_FLOAT;
+	  dotted_float = TRUE;
+	  ch = json_scanner_get_char (scanner, line_p, position_p);
+	  goto number_parsing;
+	  
+	case '$':
+	  if (!config->scan_hex_dollar)
+	    goto default_case;
+	  token = G_TOKEN_HEX;
+	  ch = json_scanner_get_char (scanner, line_p, position_p);
+	  goto number_parsing;
+	  
+	case '0':
+	  if (config->scan_octal)
+	    token = G_TOKEN_OCTAL;
+	  else
+	    token = G_TOKEN_INT;
+	  ch = json_scanner_peek_next_char (scanner);
+	  if (config->scan_hex && (ch == 'x' || ch == 'X'))
+	    {
+	      token = G_TOKEN_HEX;
+	      json_scanner_get_char (scanner, line_p, position_p);
+	      ch = json_scanner_get_char (scanner, line_p, position_p);
+	      if (ch == 0)
+		{
+		  token = G_TOKEN_ERROR;
+		  value.v_error = G_ERR_UNEXP_EOF;
+		  (*position_p)++;
+		  break;
+		}
+	      if (json_scanner_char_2_num (ch, 16) < 0)
+		{
+		  token = G_TOKEN_ERROR;
+		  value.v_error = G_ERR_DIGIT_RADIX;
+		  ch = 0;
+		  break;
+		}
+	    }
+	  else if (config->scan_binary && (ch == 'b' || ch == 'B'))
+	    {
+	      token = G_TOKEN_BINARY;
+	      json_scanner_get_char (scanner, line_p, position_p);
+	      ch = json_scanner_get_char (scanner, line_p, position_p);
+	      if (ch == 0)
+		{
+		  token = G_TOKEN_ERROR;
+		  value.v_error = G_ERR_UNEXP_EOF;
+		  (*position_p)++;
+		  break;
+		}
+	      if (json_scanner_char_2_num (ch, 10) < 0)
+		{
+		  token = G_TOKEN_ERROR;
+		  value.v_error = G_ERR_NON_DIGIT_IN_CONST;
+		  ch = 0;
+		  break;
+		}
+	    }
+	  else
+	    ch = '0';
+	  /* fall through */
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	number_parsing:
+	{
+          gboolean in_number = TRUE;
+	  gchar *endptr;
+	  
+	  if (token == G_TOKEN_NONE)
+	    token = G_TOKEN_INT;
+	  
+	  gstring = g_string_new (dotted_float ? "0." : "");
+	  gstring = g_string_append_c (gstring, ch);
+	  
+	  do /* while (in_number) */
+	    {
+	      gboolean is_E;
+	      
+	      is_E = token == G_TOKEN_FLOAT && (ch == 'e' || ch == 'E');
+	      
+	      ch = json_scanner_peek_next_char (scanner);
+	      
+	      if (json_scanner_char_2_num (ch, 36) >= 0 ||
+		  (config->scan_float && ch == '.') ||
+		  (is_E && (ch == '+' || ch == '-')))
+		{
+		  ch = json_scanner_get_char (scanner, line_p, position_p);
+		  
+		  switch (ch)
+		    {
+		    case '.':
+		      if (token != G_TOKEN_INT && token != G_TOKEN_OCTAL)
+			{
+			  value.v_error = token == G_TOKEN_FLOAT ? G_ERR_FLOAT_MALFORMED : G_ERR_FLOAT_RADIX;
+			  token = G_TOKEN_ERROR;
+			  in_number = FALSE;
+			}
+		      else
+			{
+			  token = G_TOKEN_FLOAT;
+			  gstring = g_string_append_c (gstring, ch);
+			}
+		      break;
+		      
+		    case '0':
+		    case '1':
+		    case '2':
+		    case '3':
+		    case '4':
+		    case '5':
+		    case '6':
+		    case '7':
+		    case '8':
+		    case '9':
+		      gstring = g_string_append_c (gstring, ch);
+		      break;
+		      
+		    case '-':
+		    case '+':
+		      if (token != G_TOKEN_FLOAT)
+			{
+			  token = G_TOKEN_ERROR;
+			  value.v_error = G_ERR_NON_DIGIT_IN_CONST;
+			  in_number = FALSE;
+			}
+		      else
+			gstring = g_string_append_c (gstring, ch);
+		      break;
+		      
+		    case 'e':
+		    case 'E':
+		      if ((token != G_TOKEN_HEX && !config->scan_float) ||
+			  (token != G_TOKEN_HEX &&
+			   token != G_TOKEN_OCTAL &&
+			   token != G_TOKEN_FLOAT &&
+			   token != G_TOKEN_INT))
+			{
+			  token = G_TOKEN_ERROR;
+			  value.v_error = G_ERR_NON_DIGIT_IN_CONST;
+			  in_number = FALSE;
+			}
+		      else
+			{
+			  if (token != G_TOKEN_HEX)
+			    token = G_TOKEN_FLOAT;
+			  gstring = g_string_append_c (gstring, ch);
+			}
+		      break;
+		      
+		    default:
+		      if (token != G_TOKEN_HEX)
+			{
+			  token = G_TOKEN_ERROR;
+			  value.v_error = G_ERR_NON_DIGIT_IN_CONST;
+			  in_number = FALSE;
+			}
+		      else
+			gstring = g_string_append_c (gstring, ch);
+		      break;
+		    }
+		}
+	      else
+		in_number = FALSE;
+	    }
+	  while (in_number);
+	  
+	  endptr = NULL;
+	  if (token == G_TOKEN_FLOAT)
+	    value.v_float = g_strtod (gstring->str, &endptr);
+	  else
+	    {
+	      guint64 ui64 = 0;
+	      switch (token)
+		{
+		case G_TOKEN_BINARY:
+		  ui64 = g_ascii_strtoull (gstring->str, &endptr, 2);
+		  break;
+		case G_TOKEN_OCTAL:
+		  ui64 = g_ascii_strtoull (gstring->str, &endptr, 8);
+		  break;
+		case G_TOKEN_INT:
+		  ui64 = g_ascii_strtoull (gstring->str, &endptr, 10);
+		  break;
+		case G_TOKEN_HEX:
+		  ui64 = g_ascii_strtoull (gstring->str, &endptr, 16);
+		  break;
+		default: ;
+		}
+	      if (scanner->config->store_int64)
+		value.v_int64 = ui64;
+	      else
+		value.v_int = ui64;
+	    }
+	  if (endptr && *endptr)
+	    {
+	      token = G_TOKEN_ERROR;
+	      if (*endptr == 'e' || *endptr == 'E')
+		value.v_error = G_ERR_NON_DIGIT_IN_CONST;
+	      else
+		value.v_error = G_ERR_DIGIT_RADIX;
+	    }
+	  g_string_free (gstring, TRUE);
+	  gstring = NULL;
+	  ch = 0;
+	} /* number_parsing:... */
+	break;
+	
+	default:
+	default_case:
+	{
+	  if (config->cpair_comment_single &&
+	      ch == config->cpair_comment_single[0])
+	    {
+	      token = G_TOKEN_COMMENT_SINGLE;
+	      in_comment_single = TRUE;
+	      gstring = g_string_new (NULL);
+	      ch = json_scanner_get_char (scanner, line_p, position_p);
+	      while (ch != 0)
+		{
+		  if (ch == config->cpair_comment_single[1])
+		    {
+		      in_comment_single = FALSE;
+		      ch = 0;
+		      break;
+		    }
+		  
+		  gstring = g_string_append_c (gstring, ch);
+		  ch = json_scanner_get_char (scanner, line_p, position_p);
+		}
+	      /* ignore a missing newline at EOF for single line comments */
+	      if (in_comment_single &&
+		  config->cpair_comment_single[1] == '\n')
+		in_comment_single = FALSE;
+	    }
+	  else if (config->scan_identifier && ch &&
+		   strchr (config->cset_identifier_first, ch))
+	    {
+	    identifier_precedence:
+	      
+	      if (config->cset_identifier_nth && ch &&
+		  strchr (config->cset_identifier_nth,
+			  json_scanner_peek_next_char (scanner)))
+		{
+		  token = G_TOKEN_IDENTIFIER;
+		  gstring = g_string_new (NULL);
+		  gstring = g_string_append_c (gstring, ch);
+		  do
+		    {
+		      ch = json_scanner_get_char (scanner, line_p, position_p);
+		      gstring = g_string_append_c (gstring, ch);
+		      ch = json_scanner_peek_next_char (scanner);
+		    }
+		  while (ch && strchr (config->cset_identifier_nth, ch));
+		  ch = 0;
+		}
+	      else if (config->scan_identifier_1char)
+		{
+		  token = G_TOKEN_IDENTIFIER;
+		  value.v_identifier = g_new0 (gchar, 2);
+		  value.v_identifier[0] = ch;
+		  ch = 0;
+		}
+	    }
+	  if (ch)
+	    {
+	      if (config->char_2_token)
+		token = ch;
+	      else
+		{
+		  token = G_TOKEN_CHAR;
+		  value.v_char = ch;
+		}
+	      ch = 0;
+	    }
+	} /* default_case:... */
+	break;
+	}
+      g_assert (ch == 0 && token != G_TOKEN_NONE); /* paranoid */
+    }
+  while (ch != 0);
+  
+  if (in_comment_multi || in_comment_single ||
+      in_string_sq || in_string_dq)
+    {
+      token = G_TOKEN_ERROR;
+      if (gstring)
+	{
+	  g_string_free (gstring, TRUE);
+	  gstring = NULL;
+	}
+      (*position_p)++;
+      if (in_comment_multi || in_comment_single)
+	value.v_error = G_ERR_UNEXP_EOF_IN_COMMENT;
+      else /* (in_string_sq || in_string_dq) */
+	value.v_error = G_ERR_UNEXP_EOF_IN_STRING;
+    }
+  
+  if (gstring)
+    {
+      value.v_string = g_string_free (gstring, FALSE);
+      gstring = NULL;
+    }
+  
+  if (token == G_TOKEN_IDENTIFIER)
+    {
+      if (config->scan_symbols)
+	{
+	  JsonScannerKey *key;
+	  guint scope_id;
+	  
+	  scope_id = scanner->scope_id;
+	  key = json_scanner_lookup_internal (scanner, scope_id, value.v_identifier);
+	  if (!key && scope_id && scanner->config->scope_0_fallback)
+	    key = json_scanner_lookup_internal (scanner, 0, value.v_identifier);
+	  
+	  if (key)
+	    {
+	      g_free (value.v_identifier);
+	      token = G_TOKEN_SYMBOL;
+	      value.v_symbol = key->value;
+	    }
+	}
+      
+      if (token == G_TOKEN_IDENTIFIER &&
+	  config->scan_identifier_NULL &&
+	  strlen (value.v_identifier) == 4)
+	{
+	  gchar *null_upper = "NULL";
+	  gchar *null_lower = "null";
+	  
+	  if (scanner->config->case_sensitive)
+	    {
+	      if (value.v_identifier[0] == null_upper[0] &&
+		  value.v_identifier[1] == null_upper[1] &&
+		  value.v_identifier[2] == null_upper[2] &&
+		  value.v_identifier[3] == null_upper[3])
+		token = G_TOKEN_IDENTIFIER_NULL;
+	    }
+	  else
+	    {
+	      if ((value.v_identifier[0] == null_upper[0] ||
+		   value.v_identifier[0] == null_lower[0]) &&
+		  (value.v_identifier[1] == null_upper[1] ||
+		   value.v_identifier[1] == null_lower[1]) &&
+		  (value.v_identifier[2] == null_upper[2] ||
+		   value.v_identifier[2] == null_lower[2]) &&
+		  (value.v_identifier[3] == null_upper[3] ||
+		   value.v_identifier[3] == null_lower[3]))
+		token = G_TOKEN_IDENTIFIER_NULL;
+	    }
+	}
+    }
+  
+  *token_p = token;
+  *value_p = value;
+}

Added: trunk/json-glib/json-scanner.h
==============================================================================
--- (empty file)
+++ trunk/json-glib/json-scanner.h	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,171 @@
+/* json-scanner.h: Tokenizer for JSON
+ *
+ * This file is part of JSON-GLib
+ * Copyright (C) 2008 OpenedHand
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * JsonScanner is a specialized tokenizer for JSON adapted from
+ * the GScanner tokenizer in GLib; GScanner came with this notice:
+ * 
+ * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GLib Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/. 
+ *
+ * JsonScanner: modified by Emmanuele Bassi <ebassi openedhand com>
+ */
+
+#ifndef __JSON_SCANNER_H__
+#define __JSON_SCANNER_H__
+
+#include <glib/gdataset.h>
+#include <glib/ghash.h>
+#include <glib/gscanner.h>
+
+G_BEGIN_DECLS
+
+typedef struct _JsonScanner       JsonScanner;
+typedef struct _JsonScannerConfig JsonScannerConfig;
+
+typedef void (* JsonScannerMsgFunc) (JsonScanner *scanner,
+                                     gchar       *message,
+                                     gboolean     is_error);
+
+/**
+ * JsonTokenType:
+ * @JSON_TOKEN_INVALID: marker
+ * @JSON_TOKEN_TRUE: symbol for 'true' bareword
+ * @JSON_TOKEN_FALSE: symbol for 'false' bareword
+ * @JSON_TOKEN_NULL: symbol for 'null' bareword
+ * @JSON_TOKEN_VAR: symbol for 'var' bareword
+ * @JSON_TOKEN_LAST: marker
+ *
+ * Tokens for JsonScanner-based parser, extending #GTokenType.
+ */
+typedef enum {
+  JSON_TOKEN_INVALID = G_TOKEN_LAST,
+
+  JSON_TOKEN_TRUE,
+  JSON_TOKEN_FALSE,
+  JSON_TOKEN_NULL,
+  JSON_TOKEN_VAR,
+
+  JSON_TOKEN_LAST
+} JsonTokenType;
+
+/**
+ * JsonScanner:
+ *
+ * Tokenizer scanner for JSON. See #GScanner
+ *
+ * Since: 0.6
+ */
+struct _JsonScanner
+{
+  /*< private >*/
+  /* unused fields */
+  gpointer user_data;
+  guint max_parse_errors;
+  
+  /* json_scanner_error() increments this field */
+  guint parse_errors;
+  
+  /* name of input stream, featured by the default message handler */
+  const gchar *input_name;
+  
+  /* quarked data */
+  GData *qdata;
+  
+  /* link into the scanner configuration */
+  JsonScannerConfig *config;
+  
+  /* fields filled in after json_scanner_get_next_token() */
+  GTokenType token;
+  GTokenValue value;
+  guint line;
+  guint position;
+  
+  /* fields filled in after json_scanner_peek_next_token() */
+  GTokenType next_token;
+  GTokenValue next_value;
+  guint next_line;
+  guint next_position;
+  
+  /* to be considered private */
+  GHashTable *symbol_table;
+  gint input_fd;
+  const gchar *text;
+  const gchar *text_end;
+  gchar *buffer;
+  guint scope_id;
+  
+  /* handler function for _warn and _error */
+  JsonScannerMsgFunc msg_handler;
+};
+
+JsonScanner *json_scanner_new                  (void);
+void         json_scanner_destroy              (JsonScanner *scanner);
+void         json_scanner_input_file           (JsonScanner *scanner,
+                                                gint         input_fd);
+void         json_scanner_sync_file_offset     (JsonScanner *scanner);
+void         json_scanner_input_text           (JsonScanner *scanner,
+                                                const gchar *text,
+                                                guint        text_len);
+GTokenType   json_scanner_get_next_token       (JsonScanner *scanner);
+GTokenType   json_scanner_peek_next_token      (JsonScanner *scanner);
+GTokenType   json_scanner_cur_token            (JsonScanner *scanner);
+GTokenValue  json_scanner_cur_value            (JsonScanner *scanner);
+guint        json_scanner_cur_line             (JsonScanner *scanner);
+guint        json_scanner_cur_position         (JsonScanner *scanner);
+gboolean     json_scanner_eof                  (JsonScanner *scanner);
+guint        json_scanner_set_scope            (JsonScanner *scanner,
+                                                guint        scope_id);
+void         json_scanner_scope_add_symbol     (JsonScanner *scanner,
+                                                guint        scope_id,
+                                                const gchar *symbol,
+                                                gpointer     value);
+void         json_scanner_scope_remove_symbol  (JsonScanner *scanner,
+                                                guint        scope_id,
+                                                const gchar *symbol);
+gpointer     json_scanner_scope_lookup_symbol  (JsonScanner *scanner,
+                                                guint        scope_id,
+                                                const gchar *symbol);
+void         json_scanner_scope_foreach_symbol (JsonScanner *scanner,
+                                                guint        scope_id,
+                                                GHFunc       func,
+                                                gpointer     user_data);
+gpointer     json_scanner_lookup_symbol        (JsonScanner *scanner,
+                                                const gchar *symbol);
+void         json_scanner_unexp_token          (JsonScanner *scanner,
+                                                GTokenType   expected_token,
+                                                const gchar *identifier_spec,
+                                                const gchar *symbol_spec,
+                                                const gchar *symbol_name,
+                                                const gchar *message,
+                                                gint         is_error);
+void         json_scanner_error                (JsonScanner *scanner,
+                                                const gchar *format,
+                                                ...) G_GNUC_PRINTF (2,3);
+void         json_scanner_warn                 (JsonScanner *scanner,
+                                                const gchar *format,
+                                                ...) G_GNUC_PRINTF (2,3);
+
+G_END_DECLS
+
+#endif /* __JSON_SCANNER_H__ */

Added: trunk/json-glib/tests/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/json-glib/tests/Makefile.am	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,24 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = \
+	-g				\
+	-I$(top_srcdir)			\
+	-I$(top_srcdir)/json-glib	\
+	$(JSON_DEBUG_CFLAGS)		\
+	$(JSON_CFLAGS)
+
+noinst_PROGRAMS = $(TEST_PROGS)
+progs_ldadd = $(top_builddir)/json-glib/libjson-glib-1.0.la
+
+TEST_PROGS          += array-test
+array_test_SOURCES   = array-test.c
+array_test_LDADD     = $(progs_ldadd)
+
+TEST_PROGS          += object-test
+object_test_SOURCES  = object-test.c
+object_test_LDADD    = $(progs_ldadd)
+
+TEST_PROGS          += node-test
+node_test_SOURCES    = node-test.c
+node_test_LDADD      = $(progs_ldadd)
+

Added: trunk/json-glib/tests/array-test.c
==============================================================================
--- (empty file)
+++ trunk/json-glib/tests/array-test.c	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <json-glib/json-types.h>
+
+static void
+test_empty_array (void)
+{
+  JsonArray *array = json_array_new ();
+
+  g_assert_cmpint (json_array_get_length (array), ==, 0);
+  g_assert (json_array_get_elements (array) == NULL);
+
+  json_array_unref (array);
+}
+
+static void
+test_add_element (void)
+{
+  JsonArray *array = json_array_new ();
+  JsonNode *node = json_node_new (JSON_NODE_NULL);
+
+  g_assert_cmpint (json_array_get_length (array), ==, 0);
+
+  json_array_add_element (array, node);
+  g_assert_cmpint (json_array_get_length (array), ==, 1);
+
+  node = json_array_get_element (array, 0);
+  g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_NULL);
+
+  json_array_unref (array);
+}
+
+static void
+test_remove_element (void)
+{
+  JsonArray *array = json_array_new ();
+  JsonNode *node = json_node_new (JSON_NODE_NULL);
+
+  json_array_add_element (array, node);
+
+  json_array_remove_element (array, 0);
+  g_assert_cmpint (json_array_get_length (array), ==, 0);
+
+  json_array_unref (array);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/array/empty-array", test_empty_array);
+  g_test_add_func ("/array/add-element", test_add_element);
+  g_test_add_func ("/array/remove-element", test_remove_element);
+
+  return g_test_run ();
+}

Added: trunk/json-glib/tests/node-test.c
==============================================================================
--- (empty file)
+++ trunk/json-glib/tests/node-test.c	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,71 @@
+#include <glib/gtestutils.h>
+#include <json-glib/json-types.h>
+#include <string.h>
+
+static void
+test_copy (void)
+{
+  JsonNode *node = json_node_new (JSON_NODE_NULL);
+  JsonNode *copy = json_node_copy (node);
+
+  g_assert_cmpint (node->type, ==, copy->type);
+  g_assert_cmpint (json_node_get_value_type (node), ==, json_node_get_value_type (copy));
+  g_assert_cmpstr (json_node_type_name (node), ==, json_node_type_name (copy));
+
+  json_node_free (copy);
+  json_node_free (node);
+}
+
+static void
+test_null (void)
+{
+  JsonNode *node = json_node_new (JSON_NODE_NULL);
+
+  g_assert_cmpint (node->type, ==, JSON_NODE_NULL);
+  g_assert_cmpint (json_node_get_value_type (node), ==, G_TYPE_INVALID);
+  g_assert_cmpstr (json_node_type_name (node), ==, "NULL");
+
+  json_node_free (node);
+}
+
+static void
+test_value (void)
+{
+  JsonNode *node = json_node_new (JSON_NODE_VALUE);
+  GValue value = { 0, };
+  GValue check = { 0, };
+
+  g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_VALUE);
+
+  g_value_init (&value, G_TYPE_INT);
+  g_value_set_int (&value, 42);
+
+  g_assert_cmpint (G_VALUE_TYPE (&value), ==, G_TYPE_INT);
+  g_assert_cmpint (g_value_get_int (&value), ==, 42);
+
+  json_node_set_value (node, &value);
+  json_node_get_value (node, &check);
+
+  g_assert_cmpint (G_VALUE_TYPE (&value), ==, G_VALUE_TYPE (&check));
+  g_assert_cmpint (g_value_get_int (&value), ==, g_value_get_int (&check));
+  g_assert_cmpint (G_VALUE_TYPE (&check), ==, G_TYPE_INT);
+  g_assert_cmpint (g_value_get_int (&check), ==, 42);
+
+  g_value_unset (&value);
+  g_value_unset (&check);
+  json_node_free (node);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/nodes/null-node", test_null);
+  g_test_add_func ("/nodes/copy-node", test_copy);
+  g_test_add_func ("/nodes/value", test_value);
+
+  return g_test_run ();
+}

Added: trunk/json-glib/tests/object-test.c
==============================================================================
--- (empty file)
+++ trunk/json-glib/tests/object-test.c	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <json-glib/json-types.h>
+
+static void
+test_empty_object (void)
+{
+  JsonObject *object = json_object_new ();
+
+  g_assert_cmpint (json_object_get_size (object), ==, 0);
+  g_assert (json_object_get_members (object) == NULL);
+
+  json_object_unref (object);
+}
+
+static void
+test_add_member (void)
+{
+  JsonObject *object = json_object_new ();
+  JsonNode *node = json_node_new (JSON_NODE_NULL);
+
+  g_assert_cmpint (json_object_get_size (object), ==, 0);
+
+  json_object_add_member (object, "Null", node);
+  g_assert_cmpint (json_object_get_size (object), ==, 1);
+
+  node = json_object_get_member (object, "Null");
+  g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_NULL);
+
+  json_object_unref (object);
+}
+
+static void
+test_remove_member (void)
+{
+  JsonObject *object = json_object_new ();
+  JsonNode *node = json_node_new (JSON_NODE_NULL);
+
+  json_object_add_member (object, "Null", node);
+
+  json_object_remove_member (object, "Null");
+  g_assert_cmpint (json_object_get_size (object), ==, 0);
+
+  json_object_unref (object);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/object/empty-object", test_empty_object);
+  g_test_add_func ("/object/add-member", test_add_member);
+  g_test_add_func ("/object/remove-member", test_remove_member);
+
+  return g_test_run ();
+}

Modified: trunk/tests/Makefile.am
==============================================================================
--- trunk/tests/Makefile.am	(original)
+++ trunk/tests/Makefile.am	Sat Apr 19 16:34:24 2008
@@ -1,24 +1,37 @@
-noinst_PROGRAMS = 	\
-	test-01 	\
-	test-02		\
-	test-03		\
-	test-04		\
-	test-05		\
-	test-06		\
-	test-07		\
-	test-08
-
-INCLUDES = -I$(top_srcdir)
-LDADD = $(top_builddir)/json-glib/libjson-glib-1.0.la
-
-AM_CFLAGS = $(JSON_CFLAGS)
-AM_LDFLAGS = $(JSON_LIBS)
-
-test_01_SOURCES = test-01.c
-test_02_SOURCES = test-02.c
-test_03_SOURCES = test-03.c
-test_04_SOURCES = test-04.c
-test_05_SOURCES = test-05.c
-test_06_SOURCES = test-06.c
-test_07_SOURCES = test-07.c
-test_08_SOURCES = test-08.c
+include $(top_srcdir)/Makefile.decl
+
+NULL =
+
+noinst_PROGRAMS = $(TEST_PROGS)
+
+INCLUDES = -I$(top_srcdir) $(JSON_CFLAGS)
+progs_ldadd = $(top_builddir)/json-glib/libjson-glib-1.0.la $(JSON_LIBS)
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/json-glib \
+	$(JSON_DEBUG_CFLAGS)
+
+AM_CFLAGS = -g
+
+TESTS_ENVIRONMENT = srcdir=$(srcdir)
+
+TEST_PROGS          += test-parser
+test_parser_SOURCES  = test-parser.c
+test_parser_LDADD    = $(progs_ldadd)
+
+TEST_PROGS             += test-generator
+test_generator_SOURCES  = test-generator.c
+test_generator_LDADD    = $(progs_ldadd)
+
+TEST_PROGS                    += test-serialize-simple
+test_serialize_simple_SOURCES  = test-serialize-simple.c
+test_serialize_simple_LDADD    = $(progs_ldadd)
+
+TEST_PROGS                     += test-serialize-complex
+test_serialize_complex_SOURCES  = test-serialize-complex.c
+test_serialize_complex_LDADD    = $(progs_ldadd)
+
+TEST_PROGS                  += test-serialize-full
+test_serialize_full_SOURCES  = test-serialize-full.c
+test_serialize_full_LDADD    = $(progs_ldadd)

Added: trunk/tests/test-generator.c
==============================================================================
--- (empty file)
+++ trunk/tests/test-generator.c	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,399 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <json-glib/json-glib.h>
+
+static const gchar *empty_array  = "[ ]";
+static const gchar *empty_object = "{ }";
+
+static const gchar *simple_array = "[ true, false, null, 42, \"foo\" ]"; 
+static const gchar *nested_array = "[ true, [ false, null ], 42 ]";
+
+static const gchar *simple_object = "{ \"Bool1\" : true, \"Bool2\" : false, \"Null\" : null, \"Int\" : 42, \"String\" : \"foo\" }";
+
+static void
+test_empty_array (void)
+{
+  JsonGenerator *gen = json_generator_new ();
+  JsonNode *root;
+  gchar *data;
+  gsize len;
+
+  root = json_node_new (JSON_NODE_ARRAY);
+  json_node_take_array (root, json_array_new ());
+
+  json_generator_set_root (gen, root);
+  g_object_set (gen, "pretty", FALSE, NULL);
+
+  data = json_generator_to_data (gen, &len);
+
+  g_assert_cmpint (len, ==, strlen (empty_array));
+  g_assert_cmpstr (data, ==, empty_array);
+
+  g_free (data);
+  json_node_free (root);
+  g_object_unref (gen);
+}
+
+static void
+test_empty_object (void)
+{
+  JsonGenerator *gen = json_generator_new ();
+  JsonNode *root;
+  gchar *data;
+  gsize len;
+
+  root = json_node_new (JSON_NODE_OBJECT);
+  json_node_take_object (root, json_object_new ());
+
+  json_generator_set_root (gen, root);
+  g_object_set (gen, "pretty", FALSE, NULL);
+
+  data = json_generator_to_data (gen, &len);
+
+  g_assert_cmpint (len, ==, strlen (empty_object));
+  g_assert_cmpstr (data, ==, empty_object);
+
+  g_free (data);
+  json_node_free (root);
+  g_object_unref (gen);
+}
+
+static void
+test_simple_array (void)
+{
+  JsonGenerator *generator = json_generator_new ();
+  JsonNode *root, *val;
+  JsonArray *array;
+  GValue value = { 0, };
+  gchar *data;
+  gsize len;
+
+  root = json_node_new (JSON_NODE_ARRAY);
+  array = json_array_sized_new (6);
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&value, TRUE);
+  json_node_set_value (val, &value);
+  json_array_add_element (array, val);
+  g_value_unset (&value);
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&value, FALSE);
+  json_node_set_value (val, &value);
+  json_array_add_element (array, val);
+  g_value_unset (&value);
+
+  val = json_node_new (JSON_NODE_NULL);
+  json_array_add_element (array, val);
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_INT);
+  g_value_set_int (&value, 42);
+  json_node_set_value (val, &value);
+  json_array_add_element (array, val);
+  g_value_unset (&value);
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_STRING);
+  g_value_set_string (&value, "foo");
+  json_node_set_value (val, &value);
+  json_array_add_element (array, val);
+  g_value_unset (&value);
+
+  json_node_take_array (root, array);
+  json_generator_set_root (generator, root);
+
+  g_object_set (generator, "pretty", FALSE, NULL);
+  data = json_generator_to_data (generator, &len);
+
+  if (g_test_verbose ())
+    g_print ("checking simple array `%s' (expected: %s)\n",
+             data,
+             simple_array);
+
+  g_assert_cmpint (len, ==, strlen (simple_array));
+  g_assert_cmpstr (data, ==, simple_array);
+
+  g_free (data);
+  json_node_free (root);
+  g_object_unref (generator);
+}
+
+static void
+test_nested_array (void)
+{
+  JsonGenerator *generator = json_generator_new ();
+  JsonNode *root, *val, *nested_val;
+  JsonArray *array, *nested;
+  GValue value = { 0, };
+  gchar *data;
+  gsize len;
+
+  root = json_node_new (JSON_NODE_ARRAY);
+  array = json_array_sized_new (3);
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&value, TRUE);
+  json_node_set_value (val, &value);
+  json_array_add_element (array, val);
+  g_value_unset (&value);
+
+  {
+    val = json_node_new (JSON_NODE_ARRAY);
+    nested = json_array_new ();
+
+    nested_val = json_node_new (JSON_NODE_VALUE);
+    g_value_init (&value, G_TYPE_BOOLEAN);
+    g_value_set_boolean (&value, FALSE);
+    json_node_set_value (nested_val, &value);
+    json_array_add_element (nested, nested_val);
+    g_value_unset (&value);
+
+    nested_val = json_node_new (JSON_NODE_NULL);
+    json_array_add_element (nested, nested_val);
+  
+    json_node_take_array (val, nested);
+    json_array_add_element (array, val);
+  }
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_INT);
+  g_value_set_int (&value, 42);
+  json_node_set_value (val, &value);
+  json_array_add_element (array, val);
+  g_value_unset (&value);
+
+  json_node_take_array (root, array);
+  json_generator_set_root (generator, root);
+
+  g_object_set (generator, "pretty", FALSE, NULL);
+  data = json_generator_to_data (generator, &len);
+
+  g_assert_cmpint (len, ==, strlen (nested_array));
+  g_assert_cmpstr (data, ==, nested_array);
+
+  g_free (data);
+  json_node_free (root);
+  g_object_unref (generator);
+}
+
+static void
+test_simple_object (void)
+{
+  JsonGenerator *generator = json_generator_new ();
+  JsonNode *root, *val;
+  JsonObject *object;
+  GValue value = { 0, };
+  gchar *data;
+  gsize len;
+
+  root = json_node_new (JSON_NODE_OBJECT);
+  object = json_object_new ();
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&value, TRUE);
+  json_node_set_value (val, &value);
+  json_object_add_member (object, "Bool1", val);
+  g_value_unset (&value);
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&value, FALSE);
+  json_node_set_value (val, &value);
+  json_object_add_member (object, "Bool2", val);
+  g_value_unset (&value);
+
+  val = json_node_new (JSON_NODE_NULL);
+  json_object_add_member (object, "Null", val);
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_INT);
+  g_value_set_int (&value, 42);
+  json_node_set_value (val, &value);
+  json_object_add_member (object, "Int", val);
+  g_value_unset (&value);
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_STRING);
+  g_value_set_string (&value, "foo");
+  json_node_set_value (val, &value);
+  json_object_add_member (object, "String", val);
+  g_value_unset (&value);
+
+  json_node_take_object (root, object);
+  json_generator_set_root (generator, root);
+
+  g_object_set (generator, "pretty", FALSE, NULL);
+  data = json_generator_to_data (generator, &len);
+
+  if (g_test_verbose ())
+    g_print ("checking simple array `%s' (expected: %s)\n",
+             data,
+             simple_object);
+
+  g_assert_cmpint (len, ==, strlen (simple_object));
+
+  /* we cannot compare the strings literal because JsonObject does not
+   * guarantee any ordering
+   */
+
+  g_free (data);
+  json_node_free (root);
+  g_object_unref (generator);
+}
+
+#if 0
+/* this is just overkill, but I'll add it commented out, so it
+ * can be enabled if I feel like running this just to compare
+ * the length of the strings
+ */
+static void
+test_nested_object (void)
+{
+  JsonGenerator *generator = json_generator_new ();
+  JsonNode *root, *val, *nested_val;
+  JsonObject *object, *nested;
+  JsonArray *array;
+  GValue value = { 0, };
+  gchar *data;
+  gsize len;
+
+  root = json_node_new (JSON_NODE_OBJECT);
+  object = json_object_new ();
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_STRING);
+  g_value_set_string (&value, "View from 15th Floor");
+  json_node_set_value (val, &value);
+  json_object_add_member (object, "Title", val);
+  g_value_unset (&value);
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_INT);
+  g_value_set_int (&value, 800);
+  json_node_set_value (val, &value);
+  json_object_add_member (object, "Width", val);
+  g_value_unset (&value);
+
+  val = json_node_new (JSON_NODE_VALUE);
+  g_value_init (&value, G_TYPE_INT);
+  g_value_set_int (&value, 600);
+  json_node_set_value (val, &value);
+  json_object_add_member (object, "Height", val);
+  g_value_unset (&value);
+
+  {
+    val = json_node_new (JSON_NODE_ARRAY);
+    array = json_array_new ();
+
+    nested_val = json_node_new (JSON_NODE_VALUE);
+    g_value_init (&value, G_TYPE_INT);
+    g_value_set_int (&value, 116);
+    json_node_set_value (nested_val, &value);
+    json_array_add_element (array, nested_val);
+    g_value_unset (&value);
+
+    nested_val = json_node_new (JSON_NODE_VALUE);
+    g_value_init (&value, G_TYPE_INT);
+    g_value_set_int (&value, 943);
+    json_node_set_value (nested_val, &value);
+    json_array_add_element (array, nested_val);
+    g_value_unset (&value);
+
+    nested_val = json_node_new (JSON_NODE_VALUE);
+    g_value_init (&value, G_TYPE_INT);
+    g_value_set_int (&value, 234);
+    json_node_set_value (nested_val, &value);
+    json_array_add_element (array, nested_val);
+    g_value_unset (&value);
+
+    nested_val = json_node_new (JSON_NODE_VALUE);
+    g_value_init (&value, G_TYPE_INT);
+    g_value_set_int (&value, 38793);
+    json_node_set_value (nested_val, &value);
+    json_array_add_element (array, nested_val);
+    g_value_unset (&value);
+
+    json_node_take_array (val, array);
+    json_object_add_member (object, "IDs", val);
+  }
+
+  {
+    val = json_node_new (JSON_NODE_OBJECT);
+    nested = json_object_new ();
+
+    nested_val = json_node_new (JSON_NODE_VALUE);
+    g_value_init (&value, G_TYPE_STRING);
+    g_value_set_string (&value, "http://www.example.com/image/481989943";);
+    json_node_set_value (nested_val, &value);
+    json_object_add_member (nested, "Url", nested_val);
+    g_value_unset (&value);
+
+    nested_val = json_node_new (JSON_NODE_VALUE);
+    g_value_init (&value, G_TYPE_INT);
+    g_value_set_int (&value, 125);
+    json_node_set_value (nested_val, &value);
+    json_object_add_member (nested, "Width", nested_val);
+    g_value_unset (&value);
+
+    nested_val = json_node_new (JSON_NODE_VALUE);
+    g_value_init (&value, G_TYPE_INT);
+    g_value_set_int (&value, 100);
+    json_node_set_value (nested_val, &value);
+    json_object_add_member (nested, "Height", nested_val);
+    g_value_unset (&value);
+
+    json_node_take_object (val, nested);
+    json_object_add_member (object, "Thumbnail", val);
+  }
+
+  json_node_take_object (root, object);
+  json_generator_set_root (generator, root);
+
+  g_object_set (generator, "pretty", FALSE, NULL);
+  data = json_generator_to_data (generator, &len);
+
+  if (g_test_verbose ())
+    g_print ("checking nested object `%s' (expected: %s)\n",
+             data,
+             nested_object);
+
+  g_assert_cmpint (len, ==, strlen (nested_object));
+
+  /* we cannot compare the strings literal because JsonObject does not
+   * guarantee any ordering
+   */
+
+  g_free (data);
+  json_node_free (root);
+  g_object_unref (generator);
+}
+#endif
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/json-generator/empty-array", test_empty_array);
+  g_test_add_func ("/json-generator/empty-object", test_empty_object);
+  g_test_add_func ("/json-generator/simple-array", test_simple_array);
+  g_test_add_func ("/json-generator/nested-array", test_nested_array);
+  g_test_add_func ("/json-generator/simple-object", test_simple_object);
+
+  return g_test_run ();
+}

Added: trunk/tests/test-parser.c
==============================================================================
--- (empty file)
+++ trunk/tests/test-parser.c	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,523 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <json-glib/json-glib.h>
+
+static const gchar *test_empty_string = "";
+static const gchar *test_empty_array_string = "[ ]";
+static const gchar *test_empty_object_string = "{ }";
+
+static const struct {
+  const gchar *str;
+  gint len;
+  gint element;
+  JsonNodeType type;
+  GType gtype;
+} test_simple_arrays[] = {
+  { "[ true ]",                 1, 0, JSON_NODE_VALUE, G_TYPE_BOOLEAN },
+  { "[ true, false, null ]",    3, 2, JSON_NODE_NULL,  G_TYPE_INVALID },
+  { "[ 1, 2, 3.14, \"test\" ]", 4, 3, JSON_NODE_VALUE, G_TYPE_STRING  }
+};
+
+static const gchar *test_nested_arrays[] = {
+  "[ 42, [ ], null ]",
+  "[ [ ], [ true, [ true ] ] ]",
+  "[ [ false, true, 42 ], [ true, false, 3.14 ], \"test\" ]",
+  "[ true, { } ]",
+  "[ false, { \"test\" : 42 } ]",
+  "[ { \"channel\" : \"/meta/connect\" } ]"
+};
+
+static const struct {
+  const gchar *str;
+  gint size;
+  const gchar *member;
+  JsonNodeType type;
+  GType gtype;
+} test_simple_objects[] = {
+  { "{ \"test\" : 42 }", 1, "test", JSON_NODE_VALUE, G_TYPE_INT },
+  { "{ \"foo\" : \"bar\", \"baz\" : null }", 2, "baz", JSON_NODE_NULL, G_TYPE_INVALID },
+  { "{ \"channel\" : \"/meta/connect\" }", 1, "channel", JSON_NODE_VALUE, G_TYPE_STRING }
+};
+
+static const gchar *test_nested_objects[] = {
+  "{ \"array\" : [ false, \"foo\" ], \"object\" : { \"foo\" : true } }"
+};
+
+static const gchar *test_assignments[] = {
+  "var test = [ false, false, true ]",
+  "var test = [ true, 42 ];",
+  "var test = { \"foo\" : false }"
+};
+
+static const struct
+{
+  const gchar *str;
+  const gchar *member;
+  const gchar *match;
+} test_unicode[] = {
+  { "{ \"test\" : \"foo \\u00e8\" }", "test", "foo Ã" }
+};
+
+static guint n_test_simple_arrays  = G_N_ELEMENTS (test_simple_arrays);
+static guint n_test_nested_arrays  = G_N_ELEMENTS (test_nested_arrays);
+static guint n_test_simple_objects = G_N_ELEMENTS (test_simple_objects);
+static guint n_test_nested_objects = G_N_ELEMENTS (test_nested_objects);
+static guint n_test_assignments    = G_N_ELEMENTS (test_assignments);
+static guint n_test_unicode        = G_N_ELEMENTS (test_unicode);
+
+static void
+test_empty (void)
+{
+  JsonParser *parser;
+  GError *error = NULL;
+
+  parser = json_parser_new ();
+  g_assert (JSON_IS_PARSER (parser));
+
+  if (g_test_verbose ())
+    g_print ("checking json_parser_load_from_data with empty string...\n");
+
+  if (!json_parser_load_from_data (parser, test_empty_string, -1, &error))
+    {
+      if (g_test_verbose ())
+        g_print ("Error: %s\n", error->message);
+      g_error_free (error);
+      g_object_unref (parser);
+      exit (1);
+    }
+  else
+    {
+      if (g_test_verbose ())
+        g_print ("checking json_parser_get_root...\n");
+
+      g_assert (NULL == json_parser_get_root (parser));
+    }
+
+  g_object_unref (parser);
+}
+
+static void
+test_empty_array (void)
+{
+  JsonParser *parser;
+  GError *error = NULL;
+
+  parser = json_parser_new ();
+  g_assert (JSON_IS_PARSER (parser));
+
+  if (g_test_verbose ())
+    g_print ("checking json_parser_load_from_data with empty array...\n");
+
+  if (!json_parser_load_from_data (parser, test_empty_array_string, -1, &error))
+    {
+      if (g_test_verbose ())
+        g_print ("Error: %s\n", error->message);
+      g_error_free (error);
+      g_object_unref (parser);
+      exit (1);
+    }
+  else
+    {
+      JsonNode *root;
+      JsonArray *array;
+
+      g_assert (NULL != json_parser_get_root (parser));
+
+      if (g_test_verbose ())
+        g_print ("checking root node is an array...\n");
+      root = json_parser_get_root (parser);
+      g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_ARRAY);
+
+      array = json_node_get_array (root);
+      g_assert (array != NULL);
+
+      if (g_test_verbose ())
+        g_print ("checking array is empty...\n");
+      g_assert_cmpint (json_array_get_length (array), ==, 0);
+    }
+
+  g_object_unref (parser);
+}
+
+static void
+test_simple_array (void)
+{
+  gint i;
+  JsonParser *parser;
+
+  parser = json_parser_new ();
+  g_assert (JSON_IS_PARSER (parser));
+
+  if (g_test_verbose ())
+    g_print ("checking json_parser_load_from_data with simple arrays...\n");
+
+  for (i = 0; i < n_test_simple_arrays; i++)
+    {
+      GError *error = NULL;
+
+      if (!json_parser_load_from_data (parser, test_simple_arrays[i].str, -1, &error))
+        {
+          if (g_test_verbose ())
+            g_print ("Error: %s\n", error->message);
+
+          g_error_free (error);
+          g_object_unref (parser);
+          exit (1);
+        }
+      else
+        {
+          JsonNode *root, *node;
+          JsonArray *array;
+
+          g_assert (NULL != json_parser_get_root (parser));
+
+          if (g_test_verbose ())
+            g_print ("checking root node is an array...\n");
+          root = json_parser_get_root (parser);
+          g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_ARRAY);
+
+          array = json_node_get_array (root);
+          g_assert (array != NULL);
+
+         if (g_test_verbose ())
+           g_print ("checking array is of the desired length (%d)...\n",
+                    test_simple_arrays[i].len);
+         g_assert_cmpint (json_array_get_length (array), ==, test_simple_arrays[i].len);
+
+         if (g_test_verbose ())
+           g_print ("checking element %d is of the desired type %s...\n",
+                    test_simple_arrays[i].element,
+                    g_type_name (test_simple_arrays[i].gtype));
+         node = json_array_get_element (array, test_simple_arrays[i].element);
+         g_assert (node != NULL);
+         g_assert_cmpint (JSON_NODE_TYPE (node), ==, test_simple_arrays[i].type);
+         g_assert_cmpint (json_node_get_value_type (node), ==, test_simple_arrays[i].gtype);
+       }
+    }
+
+  g_object_unref (parser);
+}
+
+static void
+test_nested_array (void)
+{
+  gint i;
+  JsonParser *parser;
+
+  parser = json_parser_new ();
+  g_assert (JSON_IS_PARSER (parser));
+
+  if (g_test_verbose ())
+    g_print ("checking json_parser_load_from_data with nested arrays...\n");
+
+  for (i = 0; i < n_test_nested_arrays; i++)
+    {
+      GError *error = NULL;
+
+      if (!json_parser_load_from_data (parser, test_nested_arrays[i], -1, &error))
+        {
+          if (g_test_verbose ())
+            g_print ("Error: %s\n", error->message);
+
+          g_error_free (error);
+          g_object_unref (parser);
+          exit (1);
+        }
+      else
+        {
+          JsonNode *root;
+          JsonArray *array;
+
+          g_assert (NULL != json_parser_get_root (parser));
+
+          if (g_test_verbose ())
+            g_print ("checking root node is an array...\n");
+          root = json_parser_get_root (parser);
+          g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_ARRAY);
+
+          array = json_node_get_array (root);
+          g_assert (array != NULL);
+
+         if (g_test_verbose ())
+           g_print ("checking array is not empty...\n");
+         g_assert_cmpint (json_array_get_length (array), >, 0);
+       }
+    }
+
+  g_object_unref (parser);
+}
+
+static void
+test_empty_object (void)
+{
+  JsonParser *parser;
+  GError *error = NULL;
+
+  parser = json_parser_new ();
+  g_assert (JSON_IS_PARSER (parser));
+
+  if (g_test_verbose ())
+    g_print ("checking json_parser_load_from_data with empty object...\n");
+
+  if (!json_parser_load_from_data (parser, test_empty_object_string, -1, &error))
+    {
+      if (g_test_verbose ())
+        g_print ("Error: %s\n", error->message);
+      g_error_free (error);
+      g_object_unref (parser);
+      exit (1);
+    }
+  else
+    {
+      JsonNode *root;
+      JsonObject *object;
+
+      g_assert (NULL != json_parser_get_root (parser));
+
+      if (g_test_verbose ())
+        g_print ("checking root node is an object...\n");
+      root = json_parser_get_root (parser);
+      g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT);
+
+      object = json_node_get_object (root);
+      g_assert (object != NULL);
+
+      if (g_test_verbose ())
+        g_print ("checking object is empty...\n");
+      g_assert_cmpint (json_object_get_size (object), ==, 0);
+    }
+
+  g_object_unref (parser);
+}
+
+static void
+test_simple_object (void)
+{
+  gint i;
+  JsonParser *parser;
+
+  parser = json_parser_new ();
+  g_assert (JSON_IS_PARSER (parser));
+
+  if (g_test_verbose ())
+    g_print ("checking json_parser_load_from_data with simple objects...\n");
+
+  for (i = 0; i < n_test_simple_objects; i++)
+    {
+      GError *error = NULL;
+
+      if (!json_parser_load_from_data (parser, test_simple_objects[i].str, -1, &error))
+        {
+          if (g_test_verbose ())
+            g_print ("Error: %s\n", error->message);
+
+          g_error_free (error);
+          g_object_unref (parser);
+          exit (1);
+        }
+      else
+        {
+          JsonNode *root, *node;
+          JsonObject *object;
+
+          g_assert (NULL != json_parser_get_root (parser));
+
+          if (g_test_verbose ())
+            g_print ("checking root node is an object...\n");
+          root = json_parser_get_root (parser);
+          g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT);
+
+          object = json_node_get_object (root);
+          g_assert (object != NULL);
+
+         if (g_test_verbose ())
+           g_print ("checking object is of the desired size (%d)...\n",
+                    test_simple_objects[i].size);
+         g_assert_cmpint (json_object_get_size (object), ==, test_simple_objects[i].size);
+
+         if (g_test_verbose ())
+           g_print ("checking member '%s' is of the desired type %s...\n",
+                    test_simple_objects[i].member,
+                    g_type_name (test_simple_objects[i].gtype));
+         node = json_object_get_member (object, test_simple_objects[i].member);
+         g_assert (node != NULL);
+         g_assert_cmpint (JSON_NODE_TYPE (node), ==, test_simple_objects[i].type);
+         g_assert_cmpint (json_node_get_value_type (node), ==, test_simple_objects[i].gtype);
+       }
+    }
+
+  g_object_unref (parser);
+}
+
+static void
+test_nested_object (void)
+{
+  gint i;
+  JsonParser *parser;
+
+  parser = json_parser_new ();
+  g_assert (JSON_IS_PARSER (parser));
+
+  if (g_test_verbose ())
+    g_print ("checking json_parser_load_from_data with nested objects...\n");
+
+  for (i = 0; i < n_test_nested_objects; i++)
+    {
+      GError *error = NULL;
+
+      if (!json_parser_load_from_data (parser, test_nested_objects[i], -1, &error))
+        {
+          if (g_test_verbose ())
+            g_print ("Error: %s\n", error->message);
+
+          g_error_free (error);
+          g_object_unref (parser);
+          exit (1);
+        }
+      else
+        {
+          JsonNode *root;
+          JsonObject *object;
+
+          g_assert (NULL != json_parser_get_root (parser));
+
+          if (g_test_verbose ())
+            g_print ("checking root node is an object...\n");
+          root = json_parser_get_root (parser);
+          g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT);
+
+          object = json_node_get_object (root);
+          g_assert (object != NULL);
+
+         if (g_test_verbose ())
+           g_print ("checking object is not empty...\n");
+         g_assert_cmpint (json_object_get_size (object), >, 0);
+       }
+    }
+
+  g_object_unref (parser);
+}
+
+static void
+test_assignment (void)
+{
+  gint i;
+  JsonParser *parser;
+
+  parser = json_parser_new ();
+  g_assert (JSON_IS_PARSER (parser));
+
+  if (g_test_verbose ())
+    g_print ("checking json_parser_load_from_data with assignments...\n");
+
+  for (i = 0; i < n_test_assignments; i++)
+    {
+      GError *error = NULL;
+
+      if (!json_parser_load_from_data (parser, test_assignments[i], -1, &error))
+        {
+          if (g_test_verbose ())
+            g_print ("Error: %s\n", error->message);
+
+          g_error_free (error);
+          g_object_unref (parser);
+          exit (1);
+        }
+      else
+        {
+          gchar *var;
+
+          if (g_test_verbose ())
+            g_print ("checking assignment...\n");
+          g_assert (json_parser_has_assignment (parser, &var) == TRUE);
+          g_assert (var != NULL);
+
+          g_assert (NULL != json_parser_get_root (parser));
+       }
+    }
+
+  g_object_unref (parser);
+}
+
+static void
+test_unicode_escape (void)
+{
+  gint i;
+  JsonParser *parser;
+
+  parser = json_parser_new ();
+  g_assert (JSON_IS_PARSER (parser));
+
+  if (g_test_verbose ())
+    g_print ("checking json_parser_load_from_data with unicode escape...\n");
+
+  for (i = 0; i < n_test_unicode; i++)
+    {
+      GError *error = NULL;
+
+      if (!json_parser_load_from_data (parser, test_unicode[i].str, -1, &error))
+        {
+          if (g_test_verbose ())
+            g_print ("Error: %s\n", error->message);
+
+          g_error_free (error);
+          g_object_unref (parser);
+          exit (1);
+        }
+      else
+        {
+          JsonNode *root, *node;
+          JsonObject *object;
+
+          g_assert (NULL != json_parser_get_root (parser));
+
+          if (g_test_verbose ())
+            g_print ("checking root node is an object...\n");
+          root = json_parser_get_root (parser);
+          g_assert_cmpint (JSON_NODE_TYPE (root), ==, JSON_NODE_OBJECT);
+
+          object = json_node_get_object (root);
+          g_assert (object != NULL);
+
+         if (g_test_verbose ())
+           g_print ("checking object is not empty...\n");
+         g_assert_cmpint (json_object_get_size (object), >, 0);
+
+         node = json_object_get_member (object, test_unicode[i].member);
+         g_assert_cmpint (JSON_NODE_TYPE (node), ==, JSON_NODE_VALUE);
+
+         g_assert_cmpstr (json_node_get_string (node), ==, test_unicode[i].match);
+
+         g_assert (g_utf8_validate (json_node_get_string (node), -1, NULL));
+       }
+    }
+
+  g_object_unref (parser);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/json-parser/empty-string", test_empty);
+  g_test_add_func ("/json-parser/empty-array", test_empty_array);
+  g_test_add_func ("/json-parser/simple-array", test_simple_array);
+  g_test_add_func ("/json-parser/nested-array", test_nested_array);
+  g_test_add_func ("/json-parser/empty-object", test_empty_object);
+  g_test_add_func ("/json-parser/simple-object", test_simple_object);
+  g_test_add_func ("/json-parser/nested-object", test_nested_object);
+  g_test_add_func ("/json-parser/assignment", test_assignment);
+  g_test_add_func ("/json-parser/unicode-escape", test_unicode_escape);
+
+  return g_test_run ();
+}

Added: trunk/tests/test-serialize-complex.c
==============================================================================
--- (empty file)
+++ trunk/tests/test-serialize-complex.c	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,261 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#include <json-glib/json-glib.h>
+#include <json-glib/json-gobject.h>
+
+#define TEST_TYPE_BOXED                 (test_boxed_get_type ())
+#define TEST_TYPE_OBJECT                (test_object_get_type ())
+#define TEST_OBJECT(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject))
+#define TEST_IS_OBJECT(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
+#define TEST_IS_OBJECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass))
+
+typedef struct _TestBoxed               TestBoxed;
+typedef struct _TestObject              TestObject;
+typedef struct _TestObjectClass         TestObjectClass;
+
+struct _TestBoxed
+{
+  gint foo;
+  gboolean bar;
+};
+
+struct _TestObject
+{
+  GObject parent_instance;
+
+  gint foo;
+  gboolean bar;
+  gchar *baz;
+  TestBoxed blah;
+};
+
+struct _TestObjectClass
+{
+  GObjectClass parent_class;
+};
+
+GType test_object_get_type (void);
+
+/*** implementation ***/
+
+static TestBoxed *
+test_boxed_copy (const TestBoxed *src)
+{
+  TestBoxed *copy = g_slice_new (TestBoxed);
+
+  *copy = *src;
+
+  return copy;
+}
+
+static void
+test_boxed_free (TestBoxed *boxed)
+{
+  if (G_LIKELY (boxed))
+    {
+      g_slice_free (TestBoxed, boxed);
+    }
+}
+
+GType
+test_boxed_get_type (void)
+{
+  static GType b_type = 0;
+
+  if (G_UNLIKELY (b_type == 0))
+    b_type = g_boxed_type_register_static ("TestBoxed",
+                                           (GBoxedCopyFunc) test_boxed_copy,
+                                           (GBoxedFreeFunc) test_boxed_free);
+
+  return b_type;
+}
+
+enum
+{
+  PROP_0,
+
+  PROP_FOO,
+  PROP_BAR,
+  PROP_BAZ,
+  PROP_BLAH
+};
+
+static void json_serializable_iface_init (gpointer g_iface);
+
+G_DEFINE_TYPE_WITH_CODE (TestObject, test_object, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE,
+                                                json_serializable_iface_init));
+
+static JsonNode *
+test_object_serialize_property (JsonSerializable *serializable,
+                                const gchar      *name,
+                                const GValue     *value,
+                                GParamSpec       *pspec)
+{
+  JsonNode *retval = NULL;
+
+  if (strcmp (name, "blah") == 0)
+    {
+      TestBoxed *boxed;
+      JsonObject *obj;
+      JsonNode *val;
+
+      retval = json_node_new (JSON_NODE_OBJECT);
+      obj = json_object_new ();
+      
+      boxed = g_value_get_boxed (value);
+
+      val = json_node_new (JSON_NODE_VALUE);
+      json_node_set_int (val, boxed->foo);
+      json_object_add_member (obj, "foo", val);
+
+      val = json_node_new (JSON_NODE_VALUE);
+      json_node_set_boolean (val, boxed->bar);
+      json_object_add_member (obj, "bar", val);
+
+      json_node_take_object (retval, obj);
+    }
+
+  return retval;
+}
+
+static void
+json_serializable_iface_init (gpointer g_iface)
+{
+  JsonSerializableIface *iface = g_iface;
+
+  iface->serialize_property = test_object_serialize_property;
+}
+
+static void
+test_object_finalize (GObject *gobject)
+{
+  g_free (TEST_OBJECT (gobject)->baz);
+
+  G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
+}
+
+static void
+test_object_set_property (GObject      *gobject,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_FOO:
+      TEST_OBJECT (gobject)->foo = g_value_get_int (value);
+      break;
+    case PROP_BAR:
+      TEST_OBJECT (gobject)->bar = g_value_get_boolean (value);
+      break;
+    case PROP_BAZ:
+      g_free (TEST_OBJECT (gobject)->baz);
+      TEST_OBJECT (gobject)->baz = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+test_object_get_property (GObject    *gobject,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_FOO:
+      g_value_set_int (value, TEST_OBJECT (gobject)->foo);
+      break;
+    case PROP_BAR:
+      g_value_set_boolean (value, TEST_OBJECT (gobject)->bar);
+      break;
+    case PROP_BAZ:
+      g_value_set_string (value, TEST_OBJECT (gobject)->baz);
+      break;
+    case PROP_BLAH:
+      g_value_set_boxed (value, &(TEST_OBJECT (gobject)->blah));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+test_object_class_init (TestObjectClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = test_object_set_property;
+  gobject_class->get_property = test_object_get_property;
+  gobject_class->finalize = test_object_finalize;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_FOO,
+                                   g_param_spec_int ("foo", "Foo", "Foo",
+                                                     0, G_MAXINT, 42,
+                                                     G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_BAR,
+                                   g_param_spec_boolean ("bar", "Bar", "Bar",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_BAZ,
+                                   g_param_spec_string ("baz", "Baz", "Baz",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_BLAH,
+                                   g_param_spec_boxed ("blah", "Blah", "Blah",
+                                                       TEST_TYPE_BOXED,
+                                                       G_PARAM_READABLE));
+}
+
+static void
+test_object_init (TestObject *object)
+{
+  object->foo = 42;
+  object->bar = TRUE;
+  object->baz = g_strdup ("Test");
+
+  object->blah.foo = object->foo;
+  object->blah.bar = object->bar;
+}
+
+static void
+test_serialize (void)
+{
+  TestObject *obj = g_object_new (TEST_TYPE_OBJECT, NULL);
+  gchar *data;
+  gsize len;
+
+  data = json_serialize_gobject (G_OBJECT (obj), &len);
+
+  g_assert_cmpint (len, >, 0);
+  if (g_test_verbose ())
+    g_print ("TestObject:\n%s\n", data);
+
+  g_free (data);
+  g_object_unref (obj);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/serialize/gobject-boxed", test_serialize);
+
+  return g_test_run ();
+}

Added: trunk/tests/test-serialize-full.c
==============================================================================
--- (empty file)
+++ trunk/tests/test-serialize-full.c	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,351 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#include <json-glib/json-glib.h>
+#include <json-glib/json-gobject.h>
+
+#define TEST_TYPE_ENUM                  (test_enum_get_type ())
+#define TEST_TYPE_BOXED                 (test_boxed_get_type ())
+#define TEST_TYPE_OBJECT                (test_object_get_type ())
+#define TEST_OBJECT(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject))
+#define TEST_IS_OBJECT(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
+#define TEST_IS_OBJECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass))
+
+typedef enum {
+  TEST_ENUM_FOO,
+  TEST_ENUM_BAR,
+  TEST_ENUM_BAZ
+} TestEnum;
+
+typedef struct _TestBoxed               TestBoxed;
+typedef struct _TestObject              TestObject;
+typedef struct _TestObjectClass         TestObjectClass;
+
+struct _TestBoxed
+{
+  gint foo;
+  gboolean bar;
+};
+
+struct _TestObject
+{
+  GObject parent_instance;
+
+  gint foo;
+  gboolean bar;
+  gchar *baz;
+  TestBoxed blah;
+  TestEnum meh;
+};
+
+struct _TestObjectClass
+{
+  GObjectClass parent_class;
+};
+
+GType test_object_get_type (void);
+
+/*** implementation ***/
+
+static TestBoxed *
+test_boxed_copy (const TestBoxed *src)
+{
+  TestBoxed *copy = g_slice_new (TestBoxed);
+
+  *copy = *src;
+
+  return copy;
+}
+
+static void
+test_boxed_free (TestBoxed *boxed)
+{
+  if (G_LIKELY (boxed))
+    {
+      g_slice_free (TestBoxed, boxed);
+    }
+}
+
+GType
+test_boxed_get_type (void)
+{
+  static GType b_type = 0;
+
+  if (G_UNLIKELY (b_type == 0))
+    b_type = g_boxed_type_register_static ("TestBoxed",
+                                           (GBoxedCopyFunc) test_boxed_copy,
+                                           (GBoxedFreeFunc) test_boxed_free);
+
+  return b_type;
+}
+
+GType
+test_enum_get_type (void)
+{
+  static GType e_type = 0;
+
+  if (G_UNLIKELY (e_type == 0))
+    {
+      const GEnumValue values[] = {
+        { TEST_ENUM_FOO, "TEST_ENUM_FOO", "foo" },
+        { TEST_ENUM_BAR, "TEST_ENUM_BAR", "bar" },
+        { TEST_ENUM_BAZ, "TEST_ENUM_BAZ", "baz" },
+        { 0, NULL, NULL }
+      };
+
+      e_type = g_enum_register_static ("TestEnum", values);
+    }
+
+  return e_type;
+}
+
+enum
+{
+  PROP_0,
+
+  PROP_FOO,
+  PROP_BAR,
+  PROP_BAZ,
+  PROP_BLAH,
+  PROP_MEH
+};
+
+static void json_serializable_iface_init (gpointer g_iface);
+
+G_DEFINE_TYPE_WITH_CODE (TestObject, test_object, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE,
+                                                json_serializable_iface_init));
+
+static gboolean
+test_object_deserialize_property (JsonSerializable *serializable,
+                                  const gchar      *name,
+                                  GValue           *value,
+                                  GParamSpec       *pspec,
+                                  JsonNode         *node)
+{
+  gboolean retval = FALSE;
+
+  return retval;
+}
+
+static JsonNode *
+test_object_serialize_property (JsonSerializable *serializable,
+                                const gchar      *name,
+                                const GValue     *value,
+                                GParamSpec       *pspec)
+{
+  JsonNode *retval;
+
+  if (strcmp (name, "blah") == 0)
+    {
+      TestBoxed *boxed;
+      JsonObject *obj;
+      JsonNode *val;
+
+      retval = json_node_new (JSON_NODE_OBJECT);
+      obj = json_object_new ();
+      
+      boxed = g_value_get_boxed (value);
+
+      val = json_node_new (JSON_NODE_VALUE);
+      json_node_set_int (val, boxed->foo);
+      json_object_add_member (obj, "foo", val);
+
+      val = json_node_new (JSON_NODE_VALUE);
+      json_node_set_boolean (val, boxed->bar);
+      json_object_add_member (obj, "bar", val);
+
+      json_node_take_object (retval, obj);
+
+      test_boxed_free (boxed);
+    }
+  else
+    {
+      GValue copy = { 0, };
+
+      retval = json_node_new (JSON_NODE_VALUE);
+
+      g_value_init (&copy, G_PARAM_SPEC_VALUE_TYPE (pspec));
+      g_value_copy (value, &copy);
+      json_node_set_value (retval, &copy);
+      g_value_unset (&copy);
+    }
+
+  return retval;
+}
+
+static void
+json_serializable_iface_init (gpointer g_iface)
+{
+  JsonSerializableIface *iface = g_iface;
+
+  iface->serialize_property = test_object_serialize_property;
+  iface->deserialize_property = test_object_deserialize_property;
+}
+
+static void
+test_object_finalize (GObject *gobject)
+{
+  g_free (TEST_OBJECT (gobject)->baz);
+
+  G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
+}
+
+static void
+test_object_set_property (GObject      *gobject,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_FOO:
+      TEST_OBJECT (gobject)->foo = g_value_get_int (value);
+      break;
+    case PROP_BAR:
+      TEST_OBJECT (gobject)->bar = g_value_get_boolean (value);
+      break;
+    case PROP_BAZ:
+      g_free (TEST_OBJECT (gobject)->baz);
+      TEST_OBJECT (gobject)->baz = g_value_dup_string (value);
+      break;
+    case PROP_MEH:
+      TEST_OBJECT (gobject)->meh = g_value_get_enum (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+test_object_get_property (GObject    *gobject,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_FOO:
+      g_value_set_int (value, TEST_OBJECT (gobject)->foo);
+      break;
+    case PROP_BAR:
+      g_value_set_boolean (value, TEST_OBJECT (gobject)->bar);
+      break;
+    case PROP_BAZ:
+      g_value_set_string (value, TEST_OBJECT (gobject)->baz);
+      break;
+    case PROP_BLAH:
+      g_value_set_boxed (value, &(TEST_OBJECT (gobject)->blah));
+      break;
+    case PROP_MEH:
+      g_value_set_enum (value, TEST_OBJECT (gobject)->meh);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+test_object_class_init (TestObjectClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = test_object_set_property;
+  gobject_class->get_property = test_object_get_property;
+  gobject_class->finalize = test_object_finalize;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_FOO,
+                                   g_param_spec_int ("foo", "Foo", "Foo",
+                                                     0, G_MAXINT, 42,
+                                                     G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_BAR,
+                                   g_param_spec_boolean ("bar", "Bar", "Bar",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_BAZ,
+                                   g_param_spec_string ("baz", "Baz", "Baz",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_BLAH,
+                                   g_param_spec_boxed ("blah", "Blah", "Blah",
+                                                       TEST_TYPE_BOXED,
+                                                       G_PARAM_READABLE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_MEH,
+                                   g_param_spec_enum ("meh", "Meh", "Meh",
+                                                      TEST_TYPE_ENUM,
+                                                      TEST_ENUM_BAR,
+                                                      G_PARAM_READWRITE));
+}
+
+static void
+test_object_init (TestObject *object)
+{
+  object->foo = 0;
+  object->bar = TRUE;
+  object->baz = NULL; 
+
+  object->blah.foo = object->foo;
+  object->blah.bar = object->bar;
+
+  object->meh = TEST_ENUM_BAR;
+}
+
+static const gchar *var_test =
+"{\n"
+"  \"foo\" : 42,\n"
+"  \"bar\" : false,\n"
+"  \"baz\" : \"hello\",\n"
+"  \"meh\" : \"baz\"\n"
+"}";
+
+static void
+test_deserialize (void)
+{
+  GObject *object;
+  GError *error;
+
+  error = NULL;
+  object = json_construct_gobject (TEST_TYPE_OBJECT, var_test, -1, &error);
+  if (error)
+    g_error ("*** Unable to parse buffer: %s\n", error->message);
+
+  if (g_test_verbose ())
+    g_print ("*** TestObject ***\n"
+             " foo: %s\n"
+             " bar: %s\n"
+             " baz: %s\n"
+             " meh: %s\n",
+             TEST_OBJECT (object)->foo == 42            ? "<true>" : "<false>",
+             TEST_OBJECT (object)->bar == FALSE         ? "<true>" : "<false>",
+             TEST_OBJECT (object)->baz != NULL          ? "<true>" : "<false>",
+             TEST_OBJECT (object)->meh == TEST_ENUM_BAZ ? "<true>" : "<false>");
+
+  g_assert_cmpint (TEST_OBJECT (object)->foo, ==, 42);
+  g_assert_cmpint (TEST_OBJECT (object)->bar, ==, FALSE);
+  g_assert_cmpstr (TEST_OBJECT (object)->baz, ==, "hello");
+  g_assert_cmpint (TEST_OBJECT (object)->meh, ==, TEST_ENUM_BAZ);
+
+  g_object_unref (object);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/deserialize/json-to-gobject", test_deserialize);
+
+  return g_test_run ();
+}

Added: trunk/tests/test-serialize-simple.c
==============================================================================
--- (empty file)
+++ trunk/tests/test-serialize-simple.c	Sat Apr 19 16:34:24 2008
@@ -0,0 +1,163 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#include <json-glib/json-glib.h>
+#include <json-glib/json-gobject.h>
+
+#define TEST_TYPE_OBJECT                (test_object_get_type ())
+#define TEST_OBJECT(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject))
+#define TEST_IS_OBJECT(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
+#define TEST_IS_OBJECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass))
+
+typedef struct _TestObject              TestObject;
+typedef struct _TestObjectClass         TestObjectClass;
+
+struct _TestObject
+{
+  GObject parent_instance;
+
+  gint foo;
+  gboolean bar;
+  gchar *baz;
+};
+
+struct _TestObjectClass
+{
+  GObjectClass parent_class;
+};
+
+GType test_object_get_type (void);
+
+/*** implementation ***/
+
+enum
+{
+  PROP_0,
+
+  PROP_FOO,
+  PROP_BAR,
+  PROP_BAZ
+};
+
+G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT);
+
+static void
+test_object_finalize (GObject *gobject)
+{
+  g_free (TEST_OBJECT (gobject)->baz);
+
+  G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
+}
+
+static void
+test_object_set_property (GObject      *gobject,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_FOO:
+      TEST_OBJECT (gobject)->foo = g_value_get_int (value);
+      break;
+    case PROP_BAR:
+      TEST_OBJECT (gobject)->bar = g_value_get_boolean (value);
+      break;
+    case PROP_BAZ:
+      g_free (TEST_OBJECT (gobject)->baz);
+      TEST_OBJECT (gobject)->baz = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+test_object_get_property (GObject    *gobject,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_FOO:
+      g_value_set_int (value, TEST_OBJECT (gobject)->foo);
+      break;
+    case PROP_BAR:
+      g_value_set_boolean (value, TEST_OBJECT (gobject)->bar);
+      break;
+    case PROP_BAZ:
+      g_value_set_string (value, TEST_OBJECT (gobject)->baz);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+test_object_class_init (TestObjectClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = test_object_set_property;
+  gobject_class->get_property = test_object_get_property;
+  gobject_class->finalize = test_object_finalize;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_FOO,
+                                   g_param_spec_int ("foo", "Foo", "Foo",
+                                                     0, G_MAXINT, 42,
+                                                     G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_BAR,
+                                   g_param_spec_boolean ("bar", "Bar", "Bar",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_BAZ,
+                                   g_param_spec_string ("baz", "Baz", "Baz",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+}
+
+static void
+test_object_init (TestObject *object)
+{
+  object->foo = 42;
+  object->bar = TRUE;
+  object->baz = g_strdup ("Test");
+}
+
+static void
+test_serialize (void)
+{
+  TestObject *obj = g_object_new (TEST_TYPE_OBJECT, NULL);
+  gchar *data;
+  gsize len;
+
+  data = json_serialize_gobject (G_OBJECT (obj), &len);
+
+  g_assert_cmpint (len, >, 0);
+  if (g_test_verbose ())
+    g_print ("TestObject:\n%s\n", data);
+
+  g_free (data);
+  g_object_unref (obj);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/serialize/gobject", test_serialize);
+
+  return g_test_run ();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]