[libgdata/tasks-integration: 1/2] Added _gdata_feed_new_from_json definition to gdata-private.h and created function from _gdata_feed_



commit 40b97d1ae06a44c633632ffb9ac11ece1d025532
Author: Peteris Krisjanis <pecisk gmail com>
Date:   Mon Jul 8 18:29:17 2013 +0300

    Added _gdata_feed_new_from_json definition to gdata-private.h and created function from 
_gdata_feed_new_from_xml example. Needs to add soup message header check at GDataService and create actual 
GDataParsable method for parsing JSON output.
    
    Initial code of detection of content type from SoupMessage, using SoupMessageHeaders in 
__gdata_service_query. Currently _gdata_feed_new_from_json is carbon copy of xml function, as Im about to 
write JSON functions for parsing data yet.
    
    Added functions _gdata_parsable_new_from_json and _gdata_parsable_new_from_json_node to parse JSON data 
using virtual functions parse_json and post_parse_json.
    
    Added json-glib dependency to configure.ac and Makefile.am.
    
    Annoying random paste bug fixed.
    
    Wide range of changes implementing parse_json/post_parse_json for GDataParsable, GDataFeed, GDataEntry. 
Also added helper functions to GDataParser. GDataEntry will be finished in next commit, along with adding 
GDataTasks.
    
    Added more changes to GDataEntry parse_json virtual function to check for common values from JSON 
entries. Added several parser functions and error issuers to GDataParser. Fixes all over parse_json chain for 
GDataFeed->GDataParsable and GDataEntry->GDataParsable.
    
    Several updates.
    
    core: More fixes
    
    Tons of fixes to get stuff compiled without errors and warnings. Doesnt link at introspection stage 
though.
    
    Conflicts:
        gdata/services/tasks/gdata-tasks-query.c
        gdata/services/tasks/gdata-tasks-query.h
        gdata/services/tasks/gdata-tasks-service.c
        gdata/services/tasks/gdata-tasks-service.h
        gdata/services/tasks/gdata-tasks-task.c
        gdata/services/tasks/gdata-tasks-task.h
        gdata/services/tasks/gdata-tasks-tasklist.c
    
    First wave of fixing all small bugs.
    
    Conflicts:
        gdata/services/tasks/gdata-tasks-query.c
        gdata/services/tasks/gdata-tasks-query.h
        gdata/services/tasks/gdata-tasks-task.c
        gdata/services/tasks/gdata-tasks-task.h
    
    Fixing one JSON element check in GDataFeed parse_json, and removed two g_objec_unrefs for JsonReader in 
_gdata_parsable_new_from_json_node.

 Makefile.am            |    4 +-
 configure.ac           |    3 +-
 gdata/gdata-entry.c    |   58 ++++++++++++++
 gdata/gdata-feed.c     |   79 +++++++++++++++++++
 gdata/gdata-parsable.c |   93 ++++++++++++++++++++++
 gdata/gdata-parsable.h |    5 +
 gdata/gdata-parser.c   |  200 ++++++++++++++++++++++++++++++++++++++++++++++++
 gdata/gdata-parser.h   |   10 +++
 gdata/gdata-private.h  |    8 ++
 gdata/gdata-service.c  |   21 ++++-
 10 files changed, 474 insertions(+), 7 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 67d4686..4ef7da9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -487,7 +487,7 @@ EXTRA_DIST += m4/introspection.m4
 
 if HAVE_INTROSPECTION
 gdata/GData- GDATA_API_VERSION_MAJOR@  GDATA_API_VERSION_MINOR@.gir: gdata/libgdata.la
-gdata_GData_ GDATA_API_VERSION_MAJOR@_ GDATA_API_VERSION_MINOR@_gir_INCLUDES = GObject-2.0 libxml2-2.0 
Soup-2.4
+gdata_GData_ GDATA_API_VERSION_MAJOR@_ GDATA_API_VERSION_MINOR@_gir_INCLUDES = GObject-2.0 libxml2-2.0 
Soup-2.4 Json-1.0
 if ENABLE_GOA
 gdata_GData_ GDATA_API_VERSION_MAJOR@_ GDATA_API_VERSION_MINOR@_gir_INCLUDES += Goa-1.0
 endif
@@ -521,7 +521,7 @@ gdata/libgdata.vapi: gdata/GData- GDATA_API_VERSION_MAJOR@  GDATA_API_VERSION_MI
 
 VAPIGEN_VAPIS = gdata/libgdata.vapi
 
-gdata_libgdata_vapi_DEPS = libxml-2.0 libsoup-2.4
+gdata_libgdata_vapi_DEPS = libxml-2.0 libsoup-2.4 json-glib-1.0
 gdata_libgdata_vapi_METADATADIRS = $(srcdir)/gdata
 gdata_libgdata_vapi_FILES = gdata/GData- GDATA_API_VERSION_MAJOR@  GDATA_API_VERSION_MINOR@.gir
 
diff --git a/configure.ac b/configure.ac
index f111a0d..cf2054e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -39,6 +39,7 @@ SOUP_REQS=2.37.91
 OAUTH_REQS=0.9.4
 GTK_REQS=2.91.2
 GOA_REQS=3.2
+JSON_GLIB_REQS=0.15
 
 # Before making a release, the GDATA_LT_VERSION string should be modified. The string is of the form c:r:a. 
Follow these instructions sequentially:
 #
@@ -64,7 +65,7 @@ AC_SUBST(GDATA_API_VERSION)
 AC_SUBST(GDATA_API_VERSION_MAJOR)
 AC_SUBST(GDATA_API_VERSION_MINOR)
 
-GDATA_PACKAGES_PUBLIC="gobject-2.0 glib-2.0 >= $GLIB_REQS gio-2.0 >= $GIO_REQS libxml-2.0 libsoup-2.4 >= 
$SOUP_REQS"
+GDATA_PACKAGES_PUBLIC="gobject-2.0 glib-2.0 >= $GLIB_REQS gio-2.0 >= $GIO_REQS libxml-2.0 libsoup-2.4 >= 
$SOUP_REQS json-glib-1.0 >= $JSON_GLIB_REQS"
 GDATA_PACKAGES_PRIVATE="gthread-2.0 oauth >= $OAUTH_REQS"
 GDATA_PACKAGES="$GDATA_PACKAGES_PUBLIC $GDATA_PACKAGES_PRIVATE"
 AC_SUBST([GDATA_PACKAGES_PUBLIC])
diff --git a/gdata/gdata-entry.c b/gdata/gdata-entry.c
index b46cc8e..8401d6a 100644
--- a/gdata/gdata-entry.c
+++ b/gdata/gdata-entry.c
@@ -33,6 +33,7 @@
 #include <glib/gi18n-lib.h>
 #include <libxml/parser.h>
 #include <string.h>
+#include <json-glib/json-glib.h>
 
 #include "gdata-entry.h"
 #include "gdata-types.h"
@@ -55,6 +56,9 @@ static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
 static void get_xml (GDataParsable *parsable, GString *xml_string);
 static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
 static gchar *get_entry_uri (const gchar *id) G_GNUC_WARN_UNUSED_RESULT;
+static gboolean parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error);
+static gboolean post_parse_json (GDataParsable *parsable, gpointer user_data, GError **error);
+static void get_json (GDataParsable *parsable, GString *json_string);
 
 struct _GDataEntryPrivate {
        gchar *title;
@@ -112,6 +116,10 @@ gdata_entry_class_init (GDataEntryClass *klass)
        parsable_class->get_namespaces = get_namespaces;
        parsable_class->element_name = "entry";
 
+       parsable_class->parse_json = parse_json;
+       parsable_class->post_parse_json = post_parse_json;
+       parsable_class->get_json = get_json;
+       
        klass->get_entry_uri = get_entry_uri;
 
        /**
@@ -584,6 +592,56 @@ get_entry_uri (const gchar *id)
        return g_strdup (id);
 }
 
+static gboolean
+parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error)
+{
+       gboolean success;
+       GDataEntryPrivate *priv = GDATA_ENTRY (parsable)->priv;
+       
+       /* selfLink in JSON is the same as 'content' entry in XML */
+       if (gdata_parser_string_from_json_member (reader, "title", P_DEFAULT, &(priv->title), &success, 
error) == TRUE ||
+           gdata_parser_string_from_json_member (reader, "id", P_DEFAULT, &(priv->id), &success, error) == 
TRUE ||
+           gdata_parser_int64_time_from_json_member (reader, "updated", P_REQUIRED | P_NO_DUPES, 
&(priv->updated), &success, error) == TRUE) {
+                       return success;
+               } else if (strcmp (json_reader_get_member_name (reader), "selfLink") == 0) {
+                       priv->content = (gchar*) json_reader_get_string_value (reader);
+                       priv->content_is_uri = TRUE;
+                       return TRUE;
+               }
+
+       return GDATA_PARSABLE_CLASS (gdata_entry_parent_class)->parse_json (parsable, reader, user_data, 
error);
+}
+
+static gboolean
+post_parse_json (GDataParsable *parsable, gpointer user_data, GError **error)
+{
+       GDataEntryPrivate *priv = GDATA_ENTRY (parsable)->priv;
+
+       /* Reverse our lists of stuff */
+       priv->categories = g_list_reverse (priv->categories);
+       priv->links = g_list_reverse (priv->links);
+       priv->authors = g_list_reverse (priv->authors);
+
+       return TRUE;
+}
+
+static void
+get_json (GDataParsable *parsable, GString *json_string)
+{
+       GDataEntryPrivate *priv = GDATA_ENTRY (parsable)->priv;
+
+       gdata_parser_string_append_escaped (json_string, "\"title\": \"", priv->title, "\",");
+
+       if (priv->id != NULL)
+               gdata_parser_string_append_escaped (json_string, "\"id\": \"", priv->id, "\",");
+
+       if (priv->updated != -1) {
+               gchar *updated = gdata_parser_int64_to_iso8601 (priv->updated);
+               g_string_append_printf (json_string, "\"updated\": \"%s\",", updated);
+               g_free (updated);
+       }
+}
+
 /**
  * gdata_entry_new:
  * @id: (allow-none): the entry's ID, or %NULL
diff --git a/gdata/gdata-feed.c b/gdata/gdata-feed.c
index 0e51269..536652f 100644
--- a/gdata/gdata-feed.c
+++ b/gdata/gdata-feed.c
@@ -36,6 +36,7 @@
 #include <glib/gi18n-lib.h>
 #include <libxml/parser.h>
 #include <string.h>
+#include <json-glib/json-glib.h>
 
 #include "gdata-feed.h"
 #include "gdata-entry.h"
@@ -57,6 +58,9 @@ static void _gdata_feed_add_category (GDataFeed *self, GDataCategory *category);
 static void _gdata_feed_add_link (GDataFeed *self, GDataLink *link);
 static void _gdata_feed_add_author (GDataFeed *self, GDataAuthor *author);
 
+static gboolean parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error);
+static gboolean post_parse_json (GDataParsable *parsable, gpointer user_data, GError **error);
+
 struct _GDataFeedPrivate {
        GList *entries;
        gchar *title;
@@ -112,6 +116,9 @@ gdata_feed_class_init (GDataFeedClass *klass)
        parsable_class->get_namespaces = get_namespaces;
        parsable_class->element_name = "feed";
 
+       parsable_class->parse_json = parse_json;
+       parsable_class->post_parse_json = post_parse_json;
+       
        /**
         * GDataFeed:title:
         *
@@ -584,6 +591,59 @@ get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
                GDATA_PARSABLE_GET_CLASS (i->data)->get_namespaces (GDATA_PARSABLE (i->data), namespaces);
 }
 
+static gboolean
+parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error)
+{
+       guint i;
+       GDataFeed *self = GDATA_FEED (parsable);
+       ParseData *data = user_data;
+       
+       if (!strcmp (json_reader_get_member_name (reader), "items")) {
+               // loop trough elements array
+               for (i = 0; i < json_reader_count_elements (reader); i++) {
+                       json_reader_read_element (reader, i);
+                       
+                       GDataEntry *entry;
+                       GType entry_type;
+
+                       /* Allow @data to be %NULL, and assume we're parsing a vanilla feed, so that we can 
test #GDataFeed in tests/general.c.
+                        * A little hacky, but not too much so, and valuable for testing. */
+                       entry_type = (data != NULL) ? data->entry_type : GDATA_TYPE_ENTRY;
+                       // passing reader cursor to object 
+                       entry = GDATA_ENTRY (_gdata_parsable_new_from_json_node (entry_type, reader, NULL, 
error));
+                       if (entry == NULL)
+                               return FALSE;
+
+                       /* Calls the callbacks in the main thread */
+                       if (data != NULL)
+                               _gdata_feed_call_progress_callback (self, data, entry);
+                       _gdata_feed_add_entry (self, entry);
+                       g_object_unref (entry);
+                       
+                       json_reader_end_element (reader);
+               }
+       }
+       else {
+               return GDATA_PARSABLE_CLASS (gdata_feed_parent_class)->parse_json (parsable, reader, 
user_data, error);
+       }
+
+       return TRUE;
+}
+
+static gboolean
+post_parse_json (GDataParsable *parsable, gpointer user_data, GError **error)
+{
+       GDataFeedPrivate *priv = GDATA_FEED (parsable)->priv;
+
+       /* Reverse our lists of stuff */
+       priv->entries = g_list_reverse (priv->entries);
+       priv->categories = g_list_reverse (priv->categories);
+       priv->links = g_list_reverse (priv->links);
+       priv->authors = g_list_reverse (priv->authors);
+
+       return TRUE;
+}
+
 /*
  * _gdata_feed_new:
  * @title: the feed's title
@@ -632,6 +692,25 @@ _gdata_feed_new_from_xml (GType feed_type, const gchar *xml, gint length, GType
        return feed;
 }
 
+GDataFeed *
+_gdata_feed_new_from_json (GType feed_type, const gchar *json, gint length, GType entry_type,
+                          GDataQueryProgressCallback progress_callback, gpointer progress_user_data, 
gboolean is_async, GError **error)
+{
+       ParseData *data;
+       GDataFeed *feed;
+
+       g_return_val_if_fail (g_type_is_a (feed_type, GDATA_TYPE_FEED), NULL);
+       g_return_val_if_fail (json != NULL, NULL);
+       g_return_val_if_fail (g_type_is_a (entry_type, GDATA_TYPE_ENTRY), NULL);
+       g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+       data = _gdata_feed_parse_data_new (entry_type, progress_callback, progress_user_data, is_async);
+       feed = GDATA_FEED (_gdata_parsable_new_from_json (feed_type, json, length, data, error));
+       _gdata_feed_parse_data_free (data);
+
+       return feed;
+}
+
 /**
  * gdata_feed_get_entries:
  * @self: a #GDataFeed
diff --git a/gdata/gdata-parsable.c b/gdata/gdata-parsable.c
index bada35b..72484f3 100644
--- a/gdata/gdata-parsable.c
+++ b/gdata/gdata-parsable.c
@@ -36,6 +36,7 @@
 #include <glib/gi18n-lib.h>
 #include <string.h>
 #include <libxml/parser.h>
+#include <json-glib/json-glib.h>
 
 #include "gdata-parsable.h"
 #include "gdata-private.h"
@@ -51,6 +52,7 @@ static void gdata_parsable_get_property (GObject *object, guint property_id, GVa
 static void gdata_parsable_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec 
*pspec);
 static void gdata_parsable_finalize (GObject *object);
 static gboolean real_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, 
GError **error);
+static gboolean real_parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError 
**error);
 
 struct _GDataParsablePrivate {
        GString *extra_xml;
@@ -75,6 +77,7 @@ gdata_parsable_class_init (GDataParsableClass *klass)
        gobject_class->set_property = gdata_parsable_set_property;
        gobject_class->finalize = gdata_parsable_finalize;
        klass->parse_xml = real_parse_xml;
+       klass->parse_json = real_parse_json;
 
        /**
         * GDataParsable:constructed-from-xml:
@@ -178,6 +181,21 @@ real_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer us
        return TRUE;
 }
 
+static gboolean
+real_parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error)
+{
+       const gchar *name;
+       
+       /* Unhandled JSON */
+       /* FIXME Have to find a way how to extract any value in string format, now we get just name */
+       name = json_reader_get_member_name (reader);
+       
+       g_string_append (parsable->priv->extra_xml, name);
+       g_debug("Unhandled JSON in %s: %s", G_OBJECT_TYPE_NAME (parsable), name);
+       
+       return TRUE;
+}
+
 /**
  * gdata_parsable_new_from_xml:
  * @parsable_type: the type of the class represented by the XML
@@ -320,6 +338,81 @@ _gdata_parsable_new_from_xml_node (GType parsable_type, xmlDoc *doc, xmlNode *no
        return parsable;
 }
 
+GDataParsable *
+_gdata_parsable_new_from_json (GType parsable_type, const gchar *json, gint length, gpointer user_data, 
GError **error)
+{
+       JsonParser *parser;
+       JsonReader *reader;
+       GDataParsable *parsable;
+
+       g_return_val_if_fail (g_type_is_a (parsable_type, GDATA_TYPE_PARSABLE), NULL);
+       g_return_val_if_fail (json != NULL && *json != '\0', NULL);
+       g_return_val_if_fail (length >= -1, NULL);
+       g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+       if (length == -1)
+               length = strlen (json);
+
+       parser = json_parser_new ();
+       if (!json_parser_load_from_data (parser, json, length, error))
+               return NULL;
+       /* FIXME do we need explictly check if json has returned error correctly? */
+       
+       reader = json_reader_new (json_parser_get_root (parser));
+       parsable = _gdata_parsable_new_from_json_node (parsable_type, reader, user_data, error);
+       
+       g_object_unref (reader);
+       g_object_unref (parser);
+       
+       return parsable;
+}
+
+GDataParsable *
+_gdata_parsable_new_from_json_node (GType parsable_type, JsonReader *reader, gpointer user_data, GError 
**error)
+{
+       GDataParsable *parsable;
+       GDataParsableClass *klass;
+       guint i;
+       
+       g_return_val_if_fail (g_type_is_a (parsable_type, GDATA_TYPE_PARSABLE), NULL);
+       g_return_val_if_fail (reader != NULL, NULL);
+       g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+       /* indicator property which allows distinguish between locally created and server based objects */
+       /* as it is used for non-xml tasks, and adding another one for json would be dublication */
+       parsable = g_object_new (parsable_type, "constructed-from-xml", TRUE, NULL);
+
+       klass = GDATA_PARSABLE_GET_CLASS (parsable);
+       if (klass->parse_json == NULL) {
+               g_object_unref (parsable);
+               return NULL;
+       }
+
+       g_assert (klass->element_name != NULL);
+
+       /* Parse each child element */
+       for (i = 0; i < json_reader_count_members (reader); i++) {
+               g_return_val_if_fail (json_reader_read_element (reader, i), NULL);
+               if (klass->parse_json (parsable, reader, user_data, error) == FALSE) {
+                       g_object_unref (parsable);
+                       return NULL;
+               }
+               /* get out of root object */
+               /* FIXME this is much slower than proper read_member usage */
+               /* can be replaced by count_members/list_members */
+               json_reader_end_element (reader);
+       }
+
+       /* Call the post-parse function */
+       if (klass->post_parse_json != NULL &&
+           klass->post_parse_json (parsable, user_data, error) == FALSE) {
+               g_object_unref (parsable);
+               return NULL;
+       }
+
+       return parsable;
+}
+
 static void
 build_namespaces_cb (gchar *prefix, gchar *href, GString *output)
 {
diff --git a/gdata/gdata-parsable.h b/gdata/gdata-parsable.h
index ed0dc72..3c5e516 100644
--- a/gdata/gdata-parsable.h
+++ b/gdata/gdata-parsable.h
@@ -23,6 +23,7 @@
 #include <glib.h>
 #include <glib-object.h>
 #include <libxml/parser.h>
+#include <json-glib/json-glib.h>
 
 G_BEGIN_DECLS
 
@@ -91,6 +92,10 @@ typedef struct {
        void (*get_xml) (GDataParsable *parsable, GString *xml_string);
        void (*get_namespaces) (GDataParsable *parsable, GHashTable *namespaces);
 
+       gboolean (*parse_json) (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError 
**error);
+       gboolean (*post_parse_json) (GDataParsable *parsable, gpointer user_data, GError **error);
+       void (*get_json) (GDataParsable *parsable, GString *json_string);
+       
        const gchar *element_name;
        const gchar *element_namespace;
 } GDataParsableClass;
diff --git a/gdata/gdata-parser.c b/gdata/gdata-parser.c
index 57388bf..fc51907 100644
--- a/gdata/gdata-parser.c
+++ b/gdata/gdata-parser.c
@@ -23,7 +23,10 @@
 #include <glib/gi18n-lib.h>
 #include <sys/time.h>
 #include <time.h>
+#include <string.h>
 #include <libxml/parser.h>
+#include <json-glib/json-glib.h>
+
 
 #include "gdata-parser.h"
 #include "gdata-service.h"
@@ -259,6 +262,52 @@ gdata_parser_int64_from_iso8601 (const gchar *date, gint64 *_time)
        return FALSE;
 }
 
+gboolean
+gdata_parser_error_required_json_content_missing (JsonReader *reader, GError **error)
+{
+       gchar *element_string = (gchar*) json_reader_get_member_name (reader);
+
+       /* Translators: the parameter is the name of an JSON element.
+        *
+        * For example:
+        *  A 'title' element was missing required content. */
+       g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR, _("A \'%s\' element was 
missing required content."), element_string);
+       g_free (element_string);
+
+       return FALSE;
+}
+
+gboolean
+gdata_parser_error_duplicate_json_element (JsonReader *reader, GError **error)
+{
+       gchar *element_string = (gchar*) json_reader_get_member_name (reader);
+
+       /* Translators: the parameter is the name of an JSON element.
+        *
+        * For example:
+        *  A singleton element (title) was duplicated. */
+       g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR, _("A singleton element 
(%s) was duplicated."), element_string);
+       g_free (element_string);
+
+       return FALSE;
+}
+
+gboolean
+gdata_parser_error_not_iso8601_format_json (JsonReader *reader, const gchar *actual_value, GError **error)
+{
+       gchar *element_string = (gchar*) json_reader_get_member_name (reader);
+       g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
+                    /* Translators: the first parameter is the name of an JSON element,
+                     * and the second parameter is the erroneous value (which was not in ISO 8601 format).
+                     *
+                     * For example:
+                     *  The content of a 'uploaded' element ("2009-05-06 26:30Z") was not in ISO 8601 
format. */
+                    _("The content of a \'%s\' element (\"%s\") was not in ISO 8601 format."), 
element_string, actual_value);
+       g_free (element_string);
+
+       return FALSE;
+}
+
 /*
  * gdata_parser_boolean_from_property:
  * @element: the XML element which owns the property to parse
@@ -673,6 +722,157 @@ gdata_parser_object_from_element (xmlNode *element, const gchar *element_name, G
        return TRUE;
 }
 
+/*
+ * gdata_parser_string_from_json_member:
+ * @reader: #JsonReader cursor object to read JSON node from
+ * @member_name: the name of the member to parse
+ * @options: a bitwise combination of parsing options from #GDataParserOptions, or %P_NONE
+ * @output: the return location for the parsed string content
+ * @success: the return location for a value which is %TRUE if the string was parsed successfully, %FALSE if 
an error was encountered,
+ * and undefined if @element didn't match @element_name
+ * @error: a #GError, or %NULL
+ *
+ * Gets the string content of @element if its name is @element_name, subject to various checks specified by 
@options.
+ *
+ * If @element doesn't match @element_name, %FALSE will be returned, @error will be unset and @success will 
be unset.
+ *
+ * If @element matches @element_name but one of the checks specified by @options fails, %TRUE will be 
returned, @error will be set to a
+ * %GDATA_SERVICE_ERROR_PROTOCOL_ERROR error and @success will be set to %FALSE.
+ *
+ * If @element matches @element_name and all of the checks specified by @options pass, %TRUE will be 
returned, @error will be unset and
+ * @success will be set to %TRUE.
+ *
+ * The reason for returning the success of the parsing in @success is so that calls to 
gdata_parser_string_from_element() can be chained
+ * together in a large "or" statement based on their return values, for the purposes of determining whether 
any of the calls matched
+ * a given @element. If any of the calls to gdata_parser_string_from_element() return %TRUE, the value of 
@success can be examined.
+ *
+ * Return value: %TRUE if @element matched @element_name, %FALSE otherwise
+ *
+ * Since: 0.7.0
+ */
+gboolean
+gdata_parser_string_from_json_member (JsonReader *reader, const gchar *member_name, GDataParserOptions 
options,
+                                  gchar **output, gboolean *success, GError **error)
+{
+       gchar *text;
+
+       /* Check if there's such element */
+       if (strcmp (json_reader_get_member_name (reader), member_name) != 0) {
+               return FALSE;
+       }
+
+       /* Check if the output string has already been set */
+       if (options & P_NO_DUPES && *output != NULL) {
+               *success = gdata_parser_error_duplicate_json_element (reader, error);
+               return TRUE;
+       }
+
+       /* Get the string and check it for NULLness or emptiness */
+       text = (gchar*) json_reader_get_string_value (reader);
+       if ((options & P_REQUIRED && text == NULL) || (options & P_NON_EMPTY && text != NULL && *text == 
'\0')) {
+               g_free (text);
+               *success = gdata_parser_error_required_json_content_missing (reader, error);
+               return TRUE;
+       } else if (options & P_DEFAULT && (text == NULL || *text == '\0')) {
+               text = (gchar*) g_strdup ("");
+       }
+
+       /* Success! */
+       g_free (*output);
+       *output = (gchar*) text;
+       *success = TRUE;
+
+       return TRUE;
+}
+
+/*
+ * gdata_parser_int64_time_from_json_member:
+ * @reader: #JsonReader cursor object to read JSON node from
+ * @element_name: the name of the element to parse
+ * @options: a bitwise combination of parsing options from #GDataParserOptions, or %P_NONE
+ * @output: (out caller-allocates): the return location for the parsed time value
+ * @success: the return location for a value which is %TRUE if the time val was parsed successfully, %FALSE 
if an error was encountered,
+ * and undefined if @element didn't match @element_name
+ * @error: a #GError, or %NULL
+ *
+ * Gets the time value of @element if its name is @element_name, subject to various checks specified by 
@options. It expects the text content
+ * of @element to be a date or time value in ISO 8601 format. The returned time value will be a UNIX 
timestamp (seconds since the epoch).
+ *
+ * If @element doesn't match @element_name, %FALSE will be returned, @error will be unset and @success will 
be unset.
+ *
+ * If @element matches @element_name but one of the checks specified by @options fails, %TRUE will be 
returned, @error will be set to a
+ * %GDATA_SERVICE_ERROR_PROTOCOL_ERROR error and @success will be set to %FALSE.
+ *
+ * If @element matches @element_name and all of the checks specified by @options pass, %TRUE will be 
returned, @error will be unset and
+ * @success will be set to %TRUE.
+ *
+ * The reason for returning the success of the parsing in @success is so that calls to 
gdata_parser_int64_time_from_element() can be chained
+ * together in a large "or" statement based on their return values, for the purposes of determining whether 
any of the calls matched
+ * a given @element. If any of the calls to gdata_parser_int64_time_from_element() return %TRUE, the value 
of @success can be examined.
+ *
+ * Return value: %TRUE if @element matched @element_name, %FALSE otherwise
+ *
+ * Since: 0.7.0
+ */
+gboolean
+gdata_parser_int64_time_from_json_member (JsonReader *reader, const gchar *member_name, GDataParserOptions 
options,
+                                      gint64 *output, gboolean *success, GError **error)
+{
+       gchar *text;
+       GTimeVal time_val;
+
+       /* Check if there's such element */
+       if (strcmp (json_reader_get_member_name (reader), member_name) != 0) {
+               return FALSE;
+       }
+
+       /* Check if the output time val has already been set */
+       if (options & P_NO_DUPES && *output != -1) {
+               *success = gdata_parser_error_duplicate_json_element (reader, error);
+               return TRUE;
+       }
+
+       /* Get the string and check it for NULLness */
+       text = (gchar*) json_reader_get_string_value (reader);
+       if (options & P_REQUIRED && (text == NULL || *text == '\0')) {
+               g_free (text);
+               *success = gdata_parser_error_required_json_content_missing (reader, error);
+               return TRUE;
+       }
+
+       /* Attempt to parse the string as a GTimeVal */
+       if (g_time_val_from_iso8601 ((gchar*) text, &time_val) == FALSE) {
+               *success = gdata_parser_error_not_iso8601_format_json (reader, text, error);
+               g_free (text);
+               return TRUE;
+       }
+
+       *output = time_val.tv_sec;
+
+       /* Success! */
+       g_free (text);
+       *success = TRUE;
+
+       return TRUE;
+}
+
+/* FIXME API description */
+gboolean
+gdata_parser_boolean_from_json_member (JsonReader *reader, const gchar *member_name, GDataParserOptions 
options, gboolean *output, gboolean *success, GError **error)
+{
+       
+       /* Check if there's such element */
+       if (strcmp (json_reader_get_member_name (reader), member_name) != 0) {
+               return FALSE;
+       }
+       
+       /* Get the boolean */
+       /* FIXME check json reader error if it fails to read it*/
+       *output = json_reader_get_boolean_value (reader);
+
+       return TRUE;
+}
+
 void
 gdata_parser_string_append_escaped (GString *xml_string, const gchar *pre, const gchar *element_content, 
const gchar *post)
 {
diff --git a/gdata/gdata-parser.h b/gdata/gdata-parser.h
index d37b18f..a94a9b0 100644
--- a/gdata/gdata-parser.h
+++ b/gdata/gdata-parser.h
@@ -35,6 +35,10 @@ gboolean gdata_parser_error_mutexed_properties (xmlNode *element, const gchar *p
 gboolean gdata_parser_error_required_element_missing (const gchar *element_name, const gchar 
*parent_element_name, GError **error);
 gboolean gdata_parser_error_duplicate_element (xmlNode *element, GError **error);
 
+gboolean gdata_parser_error_duplicate_json_element (JsonReader *reader, GError **error);
+gboolean gdata_parser_error_required_json_content_missing (JsonReader *reader, GError **error);
+gboolean gdata_parser_error_not_iso8601_format_json (JsonReader *reader, const gchar *actual_value, GError 
**error);
+
 gboolean gdata_parser_int64_from_date (const gchar *date, gint64 *_time);
 gchar *gdata_parser_date_from_int64 (gint64 _time) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
 gchar *gdata_parser_int64_to_iso8601 (gint64 _time) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
@@ -80,6 +84,12 @@ gboolean gdata_parser_object_from_element_setter (xmlNode *element, const gchar
                                                   gboolean *success, GError **error);
 gboolean gdata_parser_object_from_element (xmlNode *element, const gchar *element_name, GDataParserOptions 
options, GType object_type,
                                            gpointer /* GDataParsable ** */ _output, gboolean *success, 
GError **error);
+gboolean gdata_parser_string_from_json_member (JsonReader *reader, const gchar *member_name, 
GDataParserOptions options,
+                                           gchar **output, gboolean *success, GError **error);
+gboolean gdata_parser_int64_time_from_json_member (JsonReader *reader, const gchar *member_name, 
GDataParserOptions options,
+                                               gint64 *output, gboolean *success, GError **error);
+gboolean gdata_parser_boolean_from_json_member (JsonReader *reader, const gchar *member_name, 
GDataParserOptions options,
+                                               gboolean *output, gboolean *success, GError **error);
 
 void gdata_parser_string_append_escaped (GString *xml_string, const gchar *pre, const gchar 
*element_content, const gchar *post);
 gchar *gdata_parser_utf8_trim_whitespace (const gchar *s) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
diff --git a/gdata/gdata-private.h b/gdata/gdata-private.h
index 73905f4..0ffcd87 100644
--- a/gdata/gdata-private.h
+++ b/gdata/gdata-private.h
@@ -79,6 +79,11 @@ G_GNUC_INTERNAL GDataParsable *_gdata_parsable_new_from_xml (GType parsable_type
                                                              GError **error) G_GNUC_WARN_UNUSED_RESULT 
G_GNUC_MALLOC;
 G_GNUC_INTERNAL GDataParsable *_gdata_parsable_new_from_xml_node (GType parsable_type, xmlDoc *doc, xmlNode 
*node, gpointer user_data,
                                                                   GError **error) G_GNUC_WARN_UNUSED_RESULT 
G_GNUC_MALLOC;
+G_GNUC_INTERNAL GDataParsable *_gdata_parsable_new_from_json (GType parsable_type, const gchar *json, gint 
length, gpointer user_data,
+                                                             GError **error) G_GNUC_WARN_UNUSED_RESULT 
G_GNUC_MALLOC;         
+G_GNUC_INTERNAL GDataParsable *_gdata_parsable_new_from_json_node (GType parsable_type, JsonReader *reader, 
gpointer user_data,
+                                                                  GError **error) G_GNUC_WARN_UNUSED_RESULT 
G_GNUC_MALLOC;
+                                                                  
 G_GNUC_INTERNAL void _gdata_parsable_get_xml (GDataParsable *self, GString *xml_string, gboolean 
declare_namespaces);
 G_GNUC_INTERNAL void _gdata_parsable_string_append_escaped (GString *xml_string, const gchar *pre, const 
gchar *element_content, const gchar *post);
 G_GNUC_INTERNAL gboolean _gdata_parsable_is_constructed_from_xml (GDataParsable *self);
@@ -88,6 +93,9 @@ G_GNUC_INTERNAL GDataFeed *_gdata_feed_new (const gchar *title, const gchar *id,
 G_GNUC_INTERNAL GDataFeed *_gdata_feed_new_from_xml (GType feed_type, const gchar *xml, gint length, GType 
entry_type,
                                                      GDataQueryProgressCallback progress_callback, gpointer 
progress_user_data, gboolean is_async,
                                                      GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+G_GNUC_INTERNAL GDataFeed *_gdata_feed_new_from_json (GType feed_type, const gchar *json, gint length, GType 
entry_type,
+                                                     GDataQueryProgressCallback progress_callback, gpointer 
progress_user_data, gboolean is_async,
+                                                     GError **error) G_GNUC_WARN_UNUSED_RESULT 
G_GNUC_MALLOC;                                                     
 G_GNUC_INTERNAL void _gdata_feed_add_entry (GDataFeed *self, GDataEntry *entry);
 G_GNUC_INTERNAL gpointer _gdata_feed_parse_data_new (GType entry_type, GDataQueryProgressCallback 
progress_callback, gpointer progress_user_data,
                                                      gboolean is_async);
diff --git a/gdata/gdata-service.c b/gdata/gdata-service.c
index 9c20aff..f9b27a4 100644
--- a/gdata/gdata-service.c
+++ b/gdata/gdata-service.c
@@ -914,17 +914,30 @@ __gdata_service_query (GDataService *self, GDataAuthorizationDomain *domain, con
                        gboolean is_async)
 {
        GDataServiceClass *klass;
-       GDataFeed *feed;
+       GDataFeed *feed = NULL;
        SoupMessage *message;
-
+       SoupMessageHeaders *headers;
+       gchar *content_type;
+       
        message = _gdata_service_query (self, domain, feed_uri, query, cancellable, error);
        if (message == NULL)
                return NULL;
 
        g_assert (message->response_body->data != NULL);
        klass = GDATA_SERVICE_GET_CLASS (self);
-       feed = _gdata_feed_new_from_xml (klass->feed_type, message->response_body->data, 
message->response_body->length, entry_type,
-                                        progress_callback, progress_user_data, is_async, error);
+       
+       headers = message->response_headers;
+       content_type = (gchar*) soup_message_headers_get_content_type (headers, NULL);
+       if (strncmp (content_type, "application/json", 16) == 0) {
+               g_debug("JSON content type detected.\n");
+               feed = _gdata_feed_new_from_json (klass->feed_type, message->response_body->data, 
message->response_body->length, entry_type,
+                                                                               progress_callback, 
progress_user_data, is_async, error);
+       } else if (strncmp (content_type, "application/atom+xml", 20) == 0) {
+               g_debug("XML content type detected.\n");
+               feed = _gdata_feed_new_from_xml (klass->feed_type, message->response_body->data, 
message->response_body->length, entry_type,
+                                                                               progress_callback, 
progress_user_data, is_async, error);
+       }
+
        g_object_unref (message);
 
        if (feed == NULL)


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