|
Packit |
4b6dd7 |
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
|
|
Packit |
4b6dd7 |
/*
|
|
Packit |
4b6dd7 |
* GData Client
|
|
Packit |
4b6dd7 |
* Copyright (C) Philip Withnall 2009–2010 <philip@tecnocode.co.uk>
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* GData Client is free software; you can redistribute it and/or
|
|
Packit |
4b6dd7 |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
4b6dd7 |
* License as published by the Free Software Foundation; either
|
|
Packit |
4b6dd7 |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* GData Client is distributed in the hope that it will be useful,
|
|
Packit |
4b6dd7 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
4b6dd7 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
4b6dd7 |
* Lesser General Public License for more details.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
4b6dd7 |
* License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/**
|
|
Packit |
4b6dd7 |
* SECTION:gdata-parsable
|
|
Packit |
4b6dd7 |
* @short_description: GData parsable object
|
|
Packit |
4b6dd7 |
* @stability: Stable
|
|
Packit |
4b6dd7 |
* @include: gdata/gdata-parsable.h
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* #GDataParsable is an abstract class allowing easy implementation of an extensible parser. It is primarily extended by #GDataFeed and #GDataEntry,
|
|
Packit |
4b6dd7 |
* both of which require XML parsing which can be extended by subclassing.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* It allows methods to be defined for handling the root XML node, each of its child nodes, and a method to be called after parsing is complete.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Since: 0.3.0
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
#include <config.h>
|
|
Packit |
4b6dd7 |
#include <glib.h>
|
|
Packit |
4b6dd7 |
#include <glib/gi18n-lib.h>
|
|
Packit |
4b6dd7 |
#include <string.h>
|
|
Packit |
4b6dd7 |
#include <libxml/parser.h>
|
|
Packit |
4b6dd7 |
#include <json-glib/json-glib.h>
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
#include "gdata-parsable.h"
|
|
Packit |
4b6dd7 |
#include "gdata-private.h"
|
|
Packit |
4b6dd7 |
#include "gdata-parser.h"
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
GQuark
|
|
Packit |
4b6dd7 |
gdata_parser_error_quark (void)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
return g_quark_from_static_string ("gdata-parser-error-quark");
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static void gdata_parsable_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
|
|
Packit |
4b6dd7 |
static void gdata_parsable_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
|
|
Packit |
4b6dd7 |
static void gdata_parsable_finalize (GObject *object);
|
|
Packit |
4b6dd7 |
static gboolean real_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error);
|
|
Packit |
4b6dd7 |
static gboolean real_parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error);
|
|
Packit |
4b6dd7 |
static const gchar *get_content_type (void);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
struct _GDataParsablePrivate {
|
|
Packit |
4b6dd7 |
/* XML stuff. */
|
|
Packit |
4b6dd7 |
GString *extra_xml;
|
|
Packit |
4b6dd7 |
GHashTable *extra_namespaces;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* JSON stuff. */
|
|
Packit |
4b6dd7 |
GHashTable/*<gchar*, owned JsonNode*>*/ *extra_json;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
gboolean constructed_from_xml;
|
|
Packit |
4b6dd7 |
};
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
enum {
|
|
Packit |
4b6dd7 |
PROP_CONSTRUCTED_FROM_XML = 1,
|
|
Packit |
4b6dd7 |
};
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
G_DEFINE_ABSTRACT_TYPE (GDataParsable, gdata_parsable, G_TYPE_OBJECT)
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static void
|
|
Packit |
4b6dd7 |
gdata_parsable_class_init (GDataParsableClass *klass)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_type_class_add_private (klass, sizeof (GDataParsablePrivate));
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
gobject_class->get_property = gdata_parsable_get_property;
|
|
Packit |
4b6dd7 |
gobject_class->set_property = gdata_parsable_set_property;
|
|
Packit |
4b6dd7 |
gobject_class->finalize = gdata_parsable_finalize;
|
|
Packit |
4b6dd7 |
klass->parse_xml = real_parse_xml;
|
|
Packit |
4b6dd7 |
klass->parse_json = real_parse_json;
|
|
Packit |
4b6dd7 |
klass->get_content_type = get_content_type;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/**
|
|
Packit |
4b6dd7 |
* GDataParsable:constructed-from-xml:
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Specifies whether the object was constructed by parsing XML or manually.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Since: 0.7.0
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
g_object_class_install_property (gobject_class, PROP_CONSTRUCTED_FROM_XML,
|
|
Packit |
4b6dd7 |
g_param_spec_boolean ("constructed-from-xml",
|
|
Packit |
4b6dd7 |
"Constructed from XML?",
|
|
Packit |
4b6dd7 |
"Specifies whether the object was constructed by parsing XML or manually.",
|
|
Packit |
4b6dd7 |
FALSE,
|
|
Packit |
4b6dd7 |
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static void
|
|
Packit |
4b6dd7 |
gdata_parsable_init (GDataParsable *self)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_PARSABLE, GDataParsablePrivate);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
self->priv->extra_xml = g_string_new ("");
|
|
Packit |
4b6dd7 |
self->priv->extra_namespaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
self->priv->extra_json = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) json_node_free);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
self->priv->constructed_from_xml = FALSE;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static void
|
|
Packit |
4b6dd7 |
gdata_parsable_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
GDataParsablePrivate *priv = GDATA_PARSABLE (object)->priv;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
switch (property_id) {
|
|
Packit |
4b6dd7 |
case PROP_CONSTRUCTED_FROM_XML:
|
|
Packit |
4b6dd7 |
g_value_set_boolean (value, priv->constructed_from_xml);
|
|
Packit |
4b6dd7 |
break;
|
|
Packit |
4b6dd7 |
default:
|
|
Packit |
4b6dd7 |
/* We don't have any other property... */
|
|
Packit |
4b6dd7 |
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
Packit |
4b6dd7 |
break;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static void
|
|
Packit |
4b6dd7 |
gdata_parsable_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
GDataParsablePrivate *priv = GDATA_PARSABLE (object)->priv;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
switch (property_id) {
|
|
Packit |
4b6dd7 |
case PROP_CONSTRUCTED_FROM_XML:
|
|
Packit |
4b6dd7 |
priv->constructed_from_xml = g_value_get_boolean (value);
|
|
Packit |
4b6dd7 |
break;
|
|
Packit |
4b6dd7 |
default:
|
|
Packit |
4b6dd7 |
/* We don't have any other property... */
|
|
Packit |
4b6dd7 |
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
Packit |
4b6dd7 |
break;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static void
|
|
Packit |
4b6dd7 |
gdata_parsable_finalize (GObject *object)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
GDataParsablePrivate *priv = GDATA_PARSABLE (object)->priv;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_string_free (priv->extra_xml, TRUE);
|
|
Packit |
4b6dd7 |
g_hash_table_destroy (priv->extra_namespaces);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_hash_table_destroy (priv->extra_json);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Chain up to the parent class */
|
|
Packit |
4b6dd7 |
G_OBJECT_CLASS (gdata_parsable_parent_class)->finalize (object);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static gboolean
|
|
Packit |
4b6dd7 |
real_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
xmlBuffer *buffer;
|
|
Packit |
4b6dd7 |
xmlNs **namespaces, **namespace;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Unhandled XML */
|
|
Packit |
4b6dd7 |
buffer = xmlBufferCreate ();
|
|
Packit |
4b6dd7 |
xmlNodeDump (buffer, doc, node, 0, 0);
|
|
Packit |
4b6dd7 |
g_string_append (parsable->priv->extra_xml, (gchar*) xmlBufferContent (buffer));
|
|
Packit |
4b6dd7 |
g_debug ("Unhandled XML in %s: %s", G_OBJECT_TYPE_NAME (parsable), (gchar*) xmlBufferContent (buffer));
|
|
Packit |
4b6dd7 |
xmlBufferFree (buffer);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Get the namespaces */
|
|
Packit |
4b6dd7 |
namespaces = xmlGetNsList (doc, node);
|
|
Packit |
4b6dd7 |
if (namespaces == NULL)
|
|
Packit |
4b6dd7 |
return TRUE;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
for (namespace = namespaces; *namespace != NULL; namespace++) {
|
|
Packit |
4b6dd7 |
if ((*namespace)->prefix != NULL) {
|
|
Packit |
4b6dd7 |
/* NOTE: These two g_strdup()s leak, but it's probably acceptable, given that it saves us
|
|
Packit |
4b6dd7 |
* g_strdup()ing every other namespace we put in @extra_namespaces. */
|
|
Packit |
4b6dd7 |
g_hash_table_insert (parsable->priv->extra_namespaces,
|
|
Packit |
4b6dd7 |
g_strdup ((gchar*) ((*namespace)->prefix)),
|
|
Packit |
4b6dd7 |
g_strdup ((gchar*) ((*namespace)->href)));
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
xmlFree (namespaces);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return TRUE;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Extract the member node. This would be a lot easier if JsonReader had an API to return
|
|
Packit |
4b6dd7 |
* the current node (regardless of whether it's a value, object or array). FIXME: bgo#707100. */
|
|
Packit |
4b6dd7 |
static JsonNode * /* transfer full */
|
|
Packit |
4b6dd7 |
_json_reader_dup_current_node (JsonReader *reader)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
JsonNode *value;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
if (json_reader_is_value (reader) == TRUE) {
|
|
Packit |
4b6dd7 |
/* Value nodes are easy. Well, ignoring the complication of nulls. */
|
|
Packit |
4b6dd7 |
if (json_reader_get_null_value (reader) == TRUE) {
|
|
Packit |
4b6dd7 |
value = json_node_new (JSON_NODE_NULL);
|
|
Packit |
4b6dd7 |
} else {
|
|
Packit |
4b6dd7 |
value = json_node_copy (json_reader_get_value (reader));
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
} else if (json_reader_is_object (reader) == TRUE) {
|
|
Packit |
4b6dd7 |
/* Object nodes require deep copies. */
|
|
Packit |
4b6dd7 |
gint i;
|
|
Packit |
4b6dd7 |
gchar **members;
|
|
Packit |
4b6dd7 |
JsonObject *obj;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
obj = json_object_new ();
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
for (i = 0, members = json_reader_list_members (reader); members[i] != NULL; i++) {
|
|
Packit |
4b6dd7 |
json_reader_read_member (reader, members[i]);
|
|
Packit |
4b6dd7 |
json_object_set_member (obj, members[i], _json_reader_dup_current_node (reader));
|
|
Packit |
4b6dd7 |
json_reader_end_member (reader);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_strfreev (members);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
value = json_node_new (JSON_NODE_OBJECT);
|
|
Packit |
4b6dd7 |
json_node_take_object (value, obj);
|
|
Packit |
4b6dd7 |
} else if (json_reader_is_array (reader) == TRUE) {
|
|
Packit |
4b6dd7 |
/* Array nodes require deep copies. */
|
|
Packit |
4b6dd7 |
gint i, elements;
|
|
Packit |
4b6dd7 |
JsonArray *arr;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
arr = json_array_new ();
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
for (i = 0, elements = json_reader_count_elements (reader); i < elements; i++) {
|
|
Packit |
4b6dd7 |
json_reader_read_element (reader, i);
|
|
Packit |
4b6dd7 |
json_array_add_element (arr, _json_reader_dup_current_node (reader));
|
|
Packit |
4b6dd7 |
json_reader_end_element (reader);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
value = json_node_new (JSON_NODE_ARRAY);
|
|
Packit |
4b6dd7 |
json_node_take_array (value, arr);
|
|
Packit |
4b6dd7 |
} else {
|
|
Packit |
4b6dd7 |
/* Uh-oh. */
|
|
Packit |
4b6dd7 |
g_assert_not_reached ();
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return value;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static gboolean
|
|
Packit |
4b6dd7 |
real_parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
gchar *json, *member_name;
|
|
Packit |
4b6dd7 |
JsonGenerator *generator;
|
|
Packit |
4b6dd7 |
JsonNode *value;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Unhandled JSON member. Save it and its value to ->extra_xml so that it's not lost if we
|
|
Packit |
4b6dd7 |
* re-upload this Parsable to the server. */
|
|
Packit |
4b6dd7 |
member_name = g_strdup (json_reader_get_member_name (reader));
|
|
Packit |
4b6dd7 |
g_assert (member_name != NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Extract a copy of the current node. */
|
|
Packit |
4b6dd7 |
value = _json_reader_dup_current_node (reader);
|
|
Packit |
4b6dd7 |
g_assert (value != NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Serialise the value for debugging. */
|
|
Packit |
4b6dd7 |
generator = json_generator_new ();
|
|
Packit |
4b6dd7 |
json_generator_set_root (generator, value);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
json = json_generator_to_data (generator, NULL);
|
|
Packit |
4b6dd7 |
g_debug ("Unhandled JSON member ‘%s’ in %s: %s", member_name, G_OBJECT_TYPE_NAME (parsable), json);
|
|
Packit |
4b6dd7 |
g_free (json);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_object_unref (generator);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Save the value. Transfer ownership of the member_name and value. */
|
|
Packit |
4b6dd7 |
g_hash_table_replace (parsable->priv->extra_json, (gpointer) member_name, (gpointer) value);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return TRUE;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static const gchar *
|
|
Packit |
4b6dd7 |
get_content_type (void) {
|
|
Packit |
4b6dd7 |
return "application/atom+xml";
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/**
|
|
Packit |
4b6dd7 |
* gdata_parsable_new_from_xml:
|
|
Packit |
4b6dd7 |
* @parsable_type: the type of the class represented by the XML
|
|
Packit |
4b6dd7 |
* @xml: the XML for just the parsable object, with full namespace declarations
|
|
Packit |
4b6dd7 |
* @length: the length of @xml, or -1
|
|
Packit |
4b6dd7 |
* @error: a #GError, or %NULL
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Creates a new #GDataParsable subclass (of the given @parsable_type) from the given @xml.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* An object of the given @parsable_type is created, and its <function>pre_parse_xml</function>, <function>parse_xml</function> and
|
|
Packit |
4b6dd7 |
* <function>post_parse_xml</function> class functions called on the XML tree obtained from @xml. <function>pre_parse_xml</function> and
|
|
Packit |
4b6dd7 |
* <function>post_parse_xml</function> are called once each on the root node of the tree, while <function>parse_xml</function> is called for
|
|
Packit |
4b6dd7 |
* each of the child nodes of the root node.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* If @length is -1, @xml will be assumed to be null-terminated.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* If an error occurs during parsing, a suitable error from #GDataParserError will be returned.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Return value: a new #GDataParsable, or %NULL; unref with g_object_unref()
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Since: 0.4.0
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
GDataParsable *
|
|
Packit |
4b6dd7 |
gdata_parsable_new_from_xml (GType parsable_type, const gchar *xml, gint length, GError **error)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (g_type_is_a (parsable_type, GDATA_TYPE_PARSABLE), NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (xml != NULL && *xml != '\0', NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (length >= -1, NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return _gdata_parsable_new_from_xml (parsable_type, xml, length, NULL, error);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
GDataParsable *
|
|
Packit |
4b6dd7 |
_gdata_parsable_new_from_xml (GType parsable_type, const gchar *xml, gint length, gpointer user_data, GError **error)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
xmlDoc *doc;
|
|
Packit |
4b6dd7 |
xmlNode *node;
|
|
Packit |
4b6dd7 |
GDataParsable *parsable;
|
|
Packit |
4b6dd7 |
static gboolean libxml_initialised = FALSE;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (g_type_is_a (parsable_type, GDATA_TYPE_PARSABLE), NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (xml != NULL && *xml != '\0', NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (length >= -1, NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Set up libxml. We do this here to avoid introducing a libgdata setup function, which would be unnecessary hassle. This is the only place
|
|
Packit |
4b6dd7 |
* that libxml can be initialised in the library. */
|
|
Packit |
4b6dd7 |
if (libxml_initialised == FALSE) {
|
|
Packit |
4b6dd7 |
/* Change the libxml memory allocation functions to be GLib's. This means we don't have to re-allocate all the strings we get from
|
|
Packit |
4b6dd7 |
* libxml, which cuts down on strdup() calls dramatically. */
|
|
Packit |
4b6dd7 |
xmlMemSetup ((xmlFreeFunc) g_free, (xmlMallocFunc) g_malloc, (xmlReallocFunc) g_realloc, (xmlStrdupFunc) g_strdup);
|
|
Packit |
4b6dd7 |
libxml_initialised = TRUE;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
if (length == -1)
|
|
Packit |
4b6dd7 |
length = strlen (xml);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Parse the XML */
|
|
Packit |
4b6dd7 |
doc = xmlReadMemory (xml, length, "/dev/null", NULL, 0);
|
|
Packit |
4b6dd7 |
if (doc == NULL) {
|
|
Packit |
4b6dd7 |
xmlError *xml_error = xmlGetLastError ();
|
|
Packit |
4b6dd7 |
g_set_error (error, GDATA_PARSER_ERROR, GDATA_PARSER_ERROR_PARSING_STRING,
|
|
Packit |
4b6dd7 |
/* Translators: the parameter is an error message */
|
|
Packit |
4b6dd7 |
_("Error parsing XML: %s"),
|
|
Packit |
4b6dd7 |
(xml_error != NULL) ? xml_error->message : NULL);
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Get the root element */
|
|
Packit |
4b6dd7 |
node = xmlDocGetRootElement (doc);
|
|
Packit |
4b6dd7 |
if (node == NULL) {
|
|
Packit |
4b6dd7 |
/* XML document's empty */
|
|
Packit |
4b6dd7 |
xmlFreeDoc (doc);
|
|
Packit |
4b6dd7 |
g_set_error (error, GDATA_PARSER_ERROR, GDATA_PARSER_ERROR_EMPTY_DOCUMENT,
|
|
Packit |
4b6dd7 |
_("Error parsing XML: %s"),
|
|
Packit |
4b6dd7 |
/* Translators: this is a dummy error message to be substituted into "Error parsing XML: %s". */
|
|
Packit |
4b6dd7 |
_("Empty document."));
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
parsable = _gdata_parsable_new_from_xml_node (parsable_type, doc, node, user_data, error);
|
|
Packit |
4b6dd7 |
xmlFreeDoc (doc);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return parsable;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
GDataParsable *
|
|
Packit |
4b6dd7 |
_gdata_parsable_new_from_xml_node (GType parsable_type, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
GDataParsable *parsable;
|
|
Packit |
4b6dd7 |
GDataParsableClass *klass;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (g_type_is_a (parsable_type, GDATA_TYPE_PARSABLE), NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (doc != NULL, NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (node != NULL, NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
parsable = g_object_new (parsable_type, "constructed-from-xml", TRUE, NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
klass = GDATA_PARSABLE_GET_CLASS (parsable);
|
|
Packit |
4b6dd7 |
if (klass->parse_xml == NULL) {
|
|
Packit |
4b6dd7 |
g_object_unref (parsable);
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_assert (klass->element_name != NULL);
|
|
Packit |
4b6dd7 |
/* TODO: See gdata-documents-entry.c:260 for an example of where the code below doesn't work */
|
|
Packit |
4b6dd7 |
/*if (xmlStrcmp (node->name, (xmlChar*) klass->element_name) != 0 ||
|
|
Packit |
4b6dd7 |
(node->ns != NULL && xmlStrcmp (node->ns->prefix, (xmlChar*) klass->element_namespace) != 0)) {
|
|
Packit |
4b6dd7 |
* No <entry> element (required) *
|
|
Packit |
4b6dd7 |
gdata_parser_error_required_element_missing (klass->element_name, "root", error);
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}*/
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Call the pre-parse function first */
|
|
Packit |
4b6dd7 |
if (klass->pre_parse_xml != NULL &&
|
|
Packit |
4b6dd7 |
klass->pre_parse_xml (parsable, doc, node, user_data, error) == FALSE) {
|
|
Packit |
4b6dd7 |
g_object_unref (parsable);
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Parse each child element */
|
|
Packit |
4b6dd7 |
node = node->children;
|
|
Packit |
4b6dd7 |
while (node != NULL) {
|
|
Packit |
4b6dd7 |
if (klass->parse_xml (parsable, doc, node, user_data, error) == FALSE) {
|
|
Packit |
4b6dd7 |
g_object_unref (parsable);
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
node = node->next;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Call the post-parse function */
|
|
Packit |
4b6dd7 |
if (klass->post_parse_xml != NULL &&
|
|
Packit |
4b6dd7 |
klass->post_parse_xml (parsable, user_data, error) == FALSE) {
|
|
Packit |
4b6dd7 |
g_object_unref (parsable);
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return parsable;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/**
|
|
Packit |
4b6dd7 |
* gdata_parsable_new_from_json:
|
|
Packit |
4b6dd7 |
* @parsable_type: the type of the class represented by the JSON
|
|
Packit |
4b6dd7 |
* @json: the JSON for just the parsable object
|
|
Packit |
4b6dd7 |
* @length: the length of @json, or -1
|
|
Packit |
4b6dd7 |
* @error: a #GError, or %NULL
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Creates a new #GDataParsable subclass (of the given @parsable_type) from the given @json.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* An object of the given @parsable_type is created, and its <function>parse_json</function> and
|
|
Packit |
4b6dd7 |
* <function>post_parse_json</function> class functions called on the JSON node obtained from @json.
|
|
Packit |
4b6dd7 |
* <function>post_parse_json</function> is called once on the root node, while <function>parse_json</function> is called for
|
|
Packit |
4b6dd7 |
* each of the node's members.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* If @length is -1, @json will be assumed to be nul-terminated.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* If an error occurs during parsing, a suitable error from #GDataParserError will be returned.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Return value: a new #GDataParsable, or %NULL; unref with g_object_unref()
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Since: 0.15.0
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
GDataParsable *
|
|
Packit |
4b6dd7 |
gdata_parsable_new_from_json (GType parsable_type, const gchar *json, gint length, GError **error)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (g_type_is_a (parsable_type, GDATA_TYPE_PARSABLE), NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (json != NULL && *json != '\0', NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (length >= -1, NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return _gdata_parsable_new_from_json (parsable_type, json, length, NULL, error);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
GDataParsable *
|
|
Packit |
4b6dd7 |
_gdata_parsable_new_from_json (GType parsable_type, const gchar *json, gint length, gpointer user_data, GError **error)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
JsonParser *parser;
|
|
Packit |
4b6dd7 |
JsonReader *reader;
|
|
Packit |
4b6dd7 |
GDataParsable *parsable;
|
|
Packit |
4b6dd7 |
GError *child_error = NULL;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (g_type_is_a (parsable_type, GDATA_TYPE_PARSABLE), NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (json != NULL && *json != '\0', NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (length >= -1, NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
if (length == -1)
|
|
Packit |
4b6dd7 |
length = strlen (json);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
parser = json_parser_new ();
|
|
Packit |
4b6dd7 |
if (!json_parser_load_from_data (parser, json, length, &child_error)) {
|
|
Packit |
4b6dd7 |
g_set_error (error, GDATA_PARSER_ERROR, GDATA_PARSER_ERROR_PARSING_STRING,
|
|
Packit |
4b6dd7 |
/* Translators: the parameter is an error message */
|
|
Packit |
4b6dd7 |
_("Error parsing JSON: %s"), child_error->message);
|
|
Packit |
4b6dd7 |
g_error_free (child_error);
|
|
Packit |
4b6dd7 |
g_object_unref (parser);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
reader = json_reader_new (json_parser_get_root (parser));
|
|
Packit |
4b6dd7 |
parsable = _gdata_parsable_new_from_json_node (parsable_type, reader, user_data, error);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_object_unref (reader);
|
|
Packit |
4b6dd7 |
g_object_unref (parser);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return parsable;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
GDataParsable *
|
|
Packit |
4b6dd7 |
_gdata_parsable_new_from_json_node (GType parsable_type, JsonReader *reader, gpointer user_data, GError **error)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
GDataParsable *parsable;
|
|
Packit |
4b6dd7 |
GDataParsableClass *klass;
|
|
Packit |
4b6dd7 |
gint i;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (g_type_is_a (parsable_type, GDATA_TYPE_PARSABLE), NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (reader != NULL, NULL);
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Indicator property which allows distinguishing between locally created and server based objects
|
|
Packit |
4b6dd7 |
* as it is used for non-XML tasks, and adding another one for JSON would be a bit pointless. */
|
|
Packit |
4b6dd7 |
parsable = g_object_new (parsable_type, "constructed-from-xml", TRUE, NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
klass = GDATA_PARSABLE_GET_CLASS (parsable);
|
|
Packit |
4b6dd7 |
g_assert (klass->parse_json != NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Check that the outermost node is an object. */
|
|
Packit |
4b6dd7 |
if (json_reader_is_object (reader) == FALSE) {
|
|
Packit |
4b6dd7 |
g_set_error (error, GDATA_PARSER_ERROR, GDATA_PARSER_ERROR_PARSING_STRING,
|
|
Packit |
4b6dd7 |
/* Translators: the parameter is an error message */
|
|
Packit |
4b6dd7 |
_("Error parsing JSON: %s"),
|
|
Packit |
4b6dd7 |
_("Outermost JSON node is not an object."));
|
|
Packit |
4b6dd7 |
g_object_unref (parsable);
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Parse each child member. This assumes the outermost node is an object. */
|
|
Packit |
4b6dd7 |
for (i = 0; i < json_reader_count_members (reader); i++) {
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (json_reader_read_element (reader, i), NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
if (klass->parse_json (parsable, reader, user_data, error) == FALSE) {
|
|
Packit |
4b6dd7 |
json_reader_end_element (reader);
|
|
Packit |
4b6dd7 |
g_object_unref (parsable);
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
json_reader_end_element (reader);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Call the post-parse function */
|
|
Packit |
4b6dd7 |
if (klass->post_parse_json != NULL &&
|
|
Packit |
4b6dd7 |
klass->post_parse_json (parsable, user_data, error) == FALSE) {
|
|
Packit |
4b6dd7 |
g_object_unref (parsable);
|
|
Packit |
4b6dd7 |
return NULL;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return parsable;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/**
|
|
Packit |
4b6dd7 |
* gdata_parsable_get_content_type:
|
|
Packit |
4b6dd7 |
* @self: a #GDataParsable
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Returns the content type upon which the #GDataParsable is built. For example, `application/atom+xml` or `application/json`.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Return value: the parsable's content type
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Since: 0.17.7
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
const gchar *
|
|
Packit |
4b6dd7 |
gdata_parsable_get_content_type (GDataParsable *self)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
GDataParsableClass *klass;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (GDATA_IS_PARSABLE (self), NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
klass = GDATA_PARSABLE_GET_CLASS (self);
|
|
Packit |
4b6dd7 |
g_assert (klass->get_content_type != NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return klass->get_content_type ();
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static void
|
|
Packit |
4b6dd7 |
build_namespaces_cb (gchar *prefix, gchar *href, GString *output)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
g_string_append_printf (output, " xmlns:%s='%s'", prefix, href);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
static gboolean
|
|
Packit |
4b6dd7 |
filter_namespaces_cb (gchar *prefix, gchar *href, GHashTable *canonical_namespaces)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
if (g_hash_table_lookup (canonical_namespaces, prefix) != NULL)
|
|
Packit |
4b6dd7 |
return TRUE;
|
|
Packit |
4b6dd7 |
return FALSE;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/**
|
|
Packit |
4b6dd7 |
* gdata_parsable_get_xml:
|
|
Packit |
4b6dd7 |
* @self: a #GDataParsable
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Builds an XML representation of the #GDataParsable in its current state, such that it could be inserted on the server. The XML is guaranteed
|
|
Packit |
4b6dd7 |
* to have all its namespaces declared properly in a self-contained fashion, and is valid for stand-alone use.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Return value: the object's XML; free with g_free()
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Since: 0.4.0
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
gchar *
|
|
Packit |
4b6dd7 |
gdata_parsable_get_xml (GDataParsable *self)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
GString *xml_string;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (GDATA_IS_PARSABLE (self), NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
xml_string = g_string_sized_new (1000);
|
|
Packit |
4b6dd7 |
g_string_append (xml_string, "");
|
|
Packit |
4b6dd7 |
_gdata_parsable_get_xml (self, xml_string, TRUE);
|
|
Packit |
4b6dd7 |
return g_string_free (xml_string, FALSE);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/*
|
|
Packit |
4b6dd7 |
* _gdata_parsable_get_xml:
|
|
Packit |
4b6dd7 |
* @self: a #GDataParsable
|
|
Packit |
4b6dd7 |
* @xml_string: a #GString to build the XML in
|
|
Packit |
4b6dd7 |
* @declare_namespaces: %TRUE if all the namespaces used in the outputted XML should be declared in the opening tag of the root element,
|
|
Packit |
4b6dd7 |
* %FALSE otherwise
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Builds an XML representation of the #GDataParsable in its current state, such that it could be inserted on the server. If @declare_namespaces is
|
|
Packit |
4b6dd7 |
* %TRUE, the XML is guaranteed to have all its namespaces declared properly in a self-contained fashion, and is valid for stand-alone use. If
|
|
Packit |
4b6dd7 |
* @declare_namespaces is %FALSE, none of the used namespaces are declared, and the XML is suitable for insertion into a larger XML tree.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Return value: the object's XML; free with g_free()
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Since: 0.4.0
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
void
|
|
Packit |
4b6dd7 |
_gdata_parsable_get_xml (GDataParsable *self, GString *xml_string, gboolean declare_namespaces)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
GDataParsableClass *klass;
|
|
Packit |
4b6dd7 |
guint length;
|
|
Packit |
4b6dd7 |
GHashTable *namespaces = NULL; /* shut up, gcc */
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_return_if_fail (GDATA_IS_PARSABLE (self));
|
|
Packit |
4b6dd7 |
g_return_if_fail (xml_string != NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
klass = GDATA_PARSABLE_GET_CLASS (self);
|
|
Packit |
4b6dd7 |
g_assert (klass->element_name != NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Get the namespaces the class uses */
|
|
Packit |
4b6dd7 |
if (declare_namespaces == TRUE && klass->get_namespaces != NULL) {
|
|
Packit |
4b6dd7 |
namespaces = g_hash_table_new (g_str_hash, g_str_equal);
|
|
Packit |
4b6dd7 |
klass->get_namespaces (self, namespaces);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Remove any duplicate extra namespaces */
|
|
Packit |
4b6dd7 |
g_hash_table_foreach_remove (self->priv->extra_namespaces, (GHRFunc) filter_namespaces_cb, namespaces);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Build up the namespace list */
|
|
Packit |
4b6dd7 |
if (klass->element_namespace != NULL)
|
|
Packit |
4b6dd7 |
g_string_append_printf (xml_string, "<%s:%s", klass->element_namespace, klass->element_name);
|
|
Packit |
4b6dd7 |
else
|
|
Packit |
4b6dd7 |
g_string_append_printf (xml_string, "<%s", klass->element_name);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* We only include the normal namespaces if we're not at the top level of XML building */
|
|
Packit |
4b6dd7 |
if (declare_namespaces == TRUE) {
|
|
Packit |
4b6dd7 |
g_string_append (xml_string, " xmlns='http://www.w3.org/2005/Atom'");
|
|
Packit |
4b6dd7 |
if (namespaces != NULL) {
|
|
Packit |
4b6dd7 |
g_hash_table_foreach (namespaces, (GHFunc) build_namespaces_cb, xml_string);
|
|
Packit |
4b6dd7 |
g_hash_table_destroy (namespaces);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_hash_table_foreach (self->priv->extra_namespaces, (GHFunc) build_namespaces_cb, xml_string);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Add anything the class thinks is suitable */
|
|
Packit |
4b6dd7 |
if (klass->pre_get_xml != NULL)
|
|
Packit |
4b6dd7 |
klass->pre_get_xml (self, xml_string);
|
|
Packit |
4b6dd7 |
g_string_append_c (xml_string, '>');
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Store the length before we close the opening tag, so we can determine whether to self-close later on */
|
|
Packit |
4b6dd7 |
length = xml_string->len;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Add the rest of the XML */
|
|
Packit |
4b6dd7 |
if (klass->get_xml != NULL)
|
|
Packit |
4b6dd7 |
klass->get_xml (self, xml_string);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Any extra XML? */
|
|
Packit |
4b6dd7 |
if (self->priv->extra_xml != NULL && self->priv->extra_xml->str != NULL)
|
|
Packit |
4b6dd7 |
g_string_append (xml_string, self->priv->extra_xml->str);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Close the element; either by self-closing the opening tag, or by writing out a closing tag */
|
|
Packit |
4b6dd7 |
if (xml_string->len == length)
|
|
Packit |
4b6dd7 |
g_string_overwrite (xml_string, length - 1, "/>");
|
|
Packit |
4b6dd7 |
else if (klass->element_namespace != NULL)
|
|
Packit |
4b6dd7 |
g_string_append_printf (xml_string, "</%s:%s>", klass->element_namespace, klass->element_name);
|
|
Packit |
4b6dd7 |
else
|
|
Packit |
4b6dd7 |
g_string_append_printf (xml_string, "</%s>", klass->element_name);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/**
|
|
Packit |
4b6dd7 |
* gdata_parsable_get_json:
|
|
Packit |
4b6dd7 |
* @self: a #GDataParsable
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Builds a JSON representation of the #GDataParsable in its current state, such that it could be inserted on the server. The JSON
|
|
Packit |
4b6dd7 |
* is valid for stand-alone use.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Return value: the object's JSON; free with g_free()
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Since: 0.15.0
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
gchar *
|
|
Packit |
4b6dd7 |
gdata_parsable_get_json (GDataParsable *self)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
JsonGenerator *generator;
|
|
Packit |
4b6dd7 |
JsonBuilder *builder;
|
|
Packit |
4b6dd7 |
JsonNode *root;
|
|
Packit |
4b6dd7 |
gchar *output;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (GDATA_IS_PARSABLE (self), NULL);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Build the JSON tree. */
|
|
Packit |
4b6dd7 |
builder = json_builder_new ();
|
|
Packit |
4b6dd7 |
_gdata_parsable_get_json (self, builder);
|
|
Packit |
4b6dd7 |
root = json_builder_get_root (builder);
|
|
Packit |
4b6dd7 |
g_object_unref (builder);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Serialise it to a string. */
|
|
Packit |
4b6dd7 |
generator = json_generator_new ();
|
|
Packit |
4b6dd7 |
json_generator_set_root (generator, root);
|
|
Packit |
4b6dd7 |
output = json_generator_to_data (generator, NULL);
|
|
Packit |
4b6dd7 |
g_object_unref (generator);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
json_node_free (root);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
return output;
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/*
|
|
Packit |
4b6dd7 |
* _gdata_parsable_get_json:
|
|
Packit |
4b6dd7 |
* @self: a #GDataParsable
|
|
Packit |
4b6dd7 |
* @builder: a #JsonBuilder to build the JSON in
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Builds a JSON representation of the #GDataParsable in its current state, such that it could be inserted on the server.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Since: 0.15.0
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
void
|
|
Packit |
4b6dd7 |
_gdata_parsable_get_json (GDataParsable *self, JsonBuilder *builder)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
GDataParsableClass *klass;
|
|
Packit |
4b6dd7 |
GHashTableIter iter;
|
|
Packit |
4b6dd7 |
gchar *member_name;
|
|
Packit |
4b6dd7 |
JsonNode *value;
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
g_return_if_fail (GDATA_IS_PARSABLE (self));
|
|
Packit |
4b6dd7 |
g_return_if_fail (JSON_IS_BUILDER (builder));
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
klass = GDATA_PARSABLE_GET_CLASS (self);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
json_builder_begin_object (builder);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Add the JSON. */
|
|
Packit |
4b6dd7 |
if (klass->get_json != NULL)
|
|
Packit |
4b6dd7 |
klass->get_json (self, builder);
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/* Any extra JSON which we couldn't parse before? */
|
|
Packit |
4b6dd7 |
g_hash_table_iter_init (&iter, self->priv->extra_json);
|
|
Packit |
4b6dd7 |
while (g_hash_table_iter_next (&iter, (gpointer *) &member_name, (gpointer *) &value) == TRUE) {
|
|
Packit |
4b6dd7 |
json_builder_set_member_name (builder, member_name);
|
|
Packit |
4b6dd7 |
json_builder_add_value (builder, json_node_copy (value)); /* transfers ownership */
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
json_builder_end_object (builder);
|
|
Packit |
4b6dd7 |
}
|
|
Packit |
4b6dd7 |
|
|
Packit |
4b6dd7 |
/*
|
|
Packit |
4b6dd7 |
* _gdata_parsable_is_constructed_from_xml:
|
|
Packit |
4b6dd7 |
* @self: a #GDataParsable
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Returns the value of #GDataParsable:constructed-from-xml.
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Return value: %TRUE if the #GDataParsable was constructed from XML, %FALSE otherwise
|
|
Packit |
4b6dd7 |
*
|
|
Packit |
4b6dd7 |
* Since: 0.7.0
|
|
Packit |
4b6dd7 |
*/
|
|
Packit |
4b6dd7 |
gboolean
|
|
Packit |
4b6dd7 |
_gdata_parsable_is_constructed_from_xml (GDataParsable *self)
|
|
Packit |
4b6dd7 |
{
|
|
Packit |
4b6dd7 |
g_return_val_if_fail (GDATA_IS_PARSABLE (self), FALSE);
|
|
Packit |
4b6dd7 |
return self->priv->constructed_from_xml;
|
|
Packit |
4b6dd7 |
}
|