summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--examples/Makefile.am3
-rw-r--r--examples/test-xml.c123
-rw-r--r--rest/Makefile.am8
-rw-r--r--rest/rest-xml-parser.c250
-rw-r--r--rest/rest-xml-parser.h54
6 files changed, 437 insertions, 5 deletions
diff --git a/configure.ac b/configure.ac
index 74f8c32..45d2259 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,6 +16,7 @@ AM_PROG_CC_C_O
PKG_CHECK_MODULES(GLIB, glib-2.0)
PKG_CHECK_MODULES(SOUP, libsoup-2.4)
PKG_CHECK_MODULES(JSON, json-glib-1.0)
+PKG_CHECK_MODULES(XML, libxml-2.0)
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
@@ -26,6 +27,9 @@ AC_SUBST(SOUP_LIBS)
AC_SUBST(JSON_CFLAGS)
AC_SUBST(JSON_LIBS)
+AC_SUBST(XML_CFLAGS)
+AC_SUBST(XML_LIBS)
+
GTK_DOC_CHECK(1.4)
localedir=${datadir}/locale
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 7c4486f..da6eb19 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1,8 +1,9 @@
-noinst_PROGRAMS = test-raw test-json
+noinst_PROGRAMS = test-raw test-json test-xml
AM_CFLAGS = $(GLIB_CFLAGS) $(SOUP_CFLAGS) $(JSON_CFLAGS) -I..
AM_LDFLAGS = $(GLIB_LIBS) $(SOUP_LIBS) $(JSON_LIBS) ../rest/librest.la
test_raw_SOURCES = test-raw.c
test_json_SOURCES = test-json.c
+test_xml_SOURCES = test-xml.c
diff --git a/examples/test-xml.c b/examples/test-xml.c
new file mode 100644
index 0000000..1b520c9
--- /dev/null
+++ b/examples/test-xml.c
@@ -0,0 +1,123 @@
+#include <rest/rest-proxy.h>
+#include <rest/rest-xml-parser.h>
+
+/* These debugging functions *leak* */
+static gchar *
+_generate_attrs_output (GHashTable *attrs)
+{
+ gchar *res = "";
+ GList *keys, *values, *l, *ll;
+
+ res = g_strconcat (res, "{ ", NULL);
+
+ keys = g_hash_table_get_keys (attrs);
+ values = g_hash_table_get_values (attrs);
+
+ for (l = keys, ll = values; l; l = l->next, ll = ll->next)
+ {
+ res = g_strconcat (res, l->data, ":", ll->data, " ", NULL);
+ }
+
+ res = g_strconcat (res, "}", NULL);
+
+ return res;
+}
+
+static void
+_rest_xml_node_output (RestXmlNode *node, gint depth)
+{
+ RestXmlNode *child;
+ GList *values;
+ GList *l;
+
+ do {
+ g_debug ("%*s[%s, %s, %s]",
+ depth,
+ "",
+ node->name,
+ node->content,
+ _generate_attrs_output (node->attrs));
+ values = g_hash_table_get_values (node->children);
+ for (l = values; l; l = l->next)
+ {
+ child = (RestXmlNode *)l->data;
+ g_debug ("%*s%s - >", depth, "", child->name);
+ _rest_xml_node_output (child, depth + 4);
+ }
+ } while ((node = node->next) != NULL);
+}
+
+static void
+proxy_call_raw_async_cb (RestProxy *proxy,
+ guint status_code,
+ const gchar *response_message,
+ GHashTable *headers,
+ const gchar *payload,
+ gssize len,
+ GObject *weak_object,
+ gpointer userdata)
+{
+ RestXmlParser *parser;
+ RestXmlNode *node;
+
+ write (1, payload, len);
+ parser = rest_xml_parser_new ();
+ node = rest_xml_parser_parse_from_data (parser, payload, len);
+
+ _rest_xml_node_output (node, 0);
+ rest_xml_node_free (node);
+ g_object_unref (parser);
+ g_main_loop_quit ((GMainLoop *)userdata);
+}
+
+gint
+main (gint argc, gchar **argv)
+{
+ RestProxy *proxy;
+ GMainLoop *loop;
+ gchar *payload;
+ gssize len;
+
+ g_type_init ();
+ g_thread_init (NULL);
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ proxy = rest_proxy_new ("http://www.flickr.com/services/rest/", FALSE);
+ rest_proxy_call_raw_async (proxy,
+ NULL,
+ "GET",
+ proxy_call_raw_async_cb,
+ NULL,
+ loop,
+ NULL,
+ "method",
+ "flickr.photos.getInfo",
+ "api_key",
+ "314691be2e63a4d58994b2be01faacfb",
+ "photo_id",
+ "2658808091",
+ NULL);
+
+ g_main_loop_run (loop);
+
+ proxy = rest_proxy_new ("http://www.flickr.com/services/rest/", FALSE);
+ rest_proxy_call_raw_async (proxy,
+ NULL,
+ "GET",
+ proxy_call_raw_async_cb,
+ NULL,
+ loop,
+ NULL,
+ "method",
+ "flickr.people.getPublicPhotos",
+ "api_key",
+ "314691be2e63a4d58994b2be01faacfb",
+ "user_id",
+ "66598853@N00",
+ NULL);
+
+ g_main_loop_run (loop);
+
+ g_main_loop_unref (loop);
+}
diff --git a/rest/Makefile.am b/rest/Makefile.am
index 69c2368..1ecb7f2 100644
--- a/rest/Makefile.am
+++ b/rest/Makefile.am
@@ -1,7 +1,7 @@
lib_LTLIBRARIES = librest.la
-librest_la_CFLAGS = $(GLIB_CFLAGS) $(SOUP_CFLAGS) $(JSON_CFLAGS)
-librest_la_LIBADD = $(GLIB_LIBS) $(SOUP_LIBS) $(JSON_CFLAGS)
-librest_la_SOURCES = rest-proxy.c
-librest_la_HEADERS = rest-proxy.h
+librest_la_CFLAGS = $(GLIB_CFLAGS) $(SOUP_CFLAGS) $(JSON_CFLAGS) $(XML_CFLAGS)
+librest_la_LIBADD = $(GLIB_LIBS) $(SOUP_LIBS) $(JSON_CFLAGS) $(XML_LIBS)
+librest_la_SOURCES = rest-proxy.c rest-xml-parser.c
+librest_la_HEADERS = rest-proxy.h rest-xml-parser.h
librest_ladir = $(includedir)/rest/rest
diff --git a/rest/rest-xml-parser.c b/rest/rest-xml-parser.c
new file mode 100644
index 0000000..b8c94c1
--- /dev/null
+++ b/rest/rest-xml-parser.c
@@ -0,0 +1,250 @@
+#include <libxml/xmlreader.h>
+#include "rest-xml-parser.h"
+
+G_DEFINE_TYPE (RestXmlParser, rest_xml_parser, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), REST_TYPE_XML_PARSER, RestXmlParserPrivate))
+
+typedef struct _RestXmlParserPrivate RestXmlParserPrivate;
+
+struct _RestXmlParserPrivate {
+ xmlTextReaderPtr reader;
+};
+
+static void
+rest_xml_parser_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+rest_xml_parser_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ switch (property_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+rest_xml_parser_dispose (GObject *object)
+{
+ if (G_OBJECT_CLASS (rest_xml_parser_parent_class)->dispose)
+ G_OBJECT_CLASS (rest_xml_parser_parent_class)->dispose (object);
+}
+
+static void
+rest_xml_parser_finalize (GObject *object)
+{
+ RestXmlParserPrivate *priv = GET_PRIVATE (object);
+
+ xmlFreeTextReader (priv->reader);
+
+ if (G_OBJECT_CLASS (rest_xml_parser_parent_class)->finalize)
+ G_OBJECT_CLASS (rest_xml_parser_parent_class)->finalize (object);
+}
+
+static void
+rest_xml_parser_class_init (RestXmlParserClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (RestXmlParserPrivate));
+
+ object_class->get_property = rest_xml_parser_get_property;
+ object_class->set_property = rest_xml_parser_set_property;
+ object_class->dispose = rest_xml_parser_dispose;
+ object_class->finalize = rest_xml_parser_finalize;
+}
+
+static void
+rest_xml_parser_init (RestXmlParser *self)
+{
+}
+
+static RestXmlNode *
+rest_xml_node_prepend (RestXmlNode *cur_node, RestXmlNode *new_node)
+{
+ g_assert (new_node->next == NULL);
+ new_node->next = cur_node;
+
+ return new_node;
+}
+
+RestXmlNode *
+rest_xml_node_new ()
+{
+ RestXmlNode *node;
+
+ node = g_slice_new0 (RestXmlNode);
+ node->children = g_hash_table_new (g_str_hash, g_str_equal);
+ node->attrs = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_free);
+
+ return node;
+}
+
+void
+rest_xml_node_free (RestXmlNode *node)
+{
+ GList *l;
+
+ for (l = g_hash_table_get_values (node->children); l; l = l->next)
+ {
+ rest_xml_node_free ((RestXmlNode *)l->data);
+ }
+
+ g_hash_table_unref (node->children);
+ g_hash_table_unref (node->attrs);
+ g_free (node->name);
+ g_free (node->content);
+ g_slice_free (RestXmlNode, node);
+}
+
+RestXmlParser *
+rest_xml_parser_new (void)
+{
+ return g_object_new (REST_TYPE_XML_PARSER, NULL);
+}
+
+RestXmlNode *
+rest_xml_parser_parse_from_data (RestXmlParser *parser,
+ const gchar *data,
+ gssize len)
+{
+ RestXmlParserPrivate *priv = GET_PRIVATE (parser);
+ RestXmlNode *cur_node = NULL;
+ RestXmlNode *new_node = NULL;
+ RestXmlNode *tmp_node = NULL;
+ RestXmlNode *root_node = NULL;
+ const gchar *name = NULL;
+ const gchar *attr_name = NULL;
+ const gchar *attr_value = NULL;
+ GQueue *nodes = NULL;
+ gint res = 0;
+
+ priv->reader = xmlReaderForMemory (data,
+ len,
+ NULL, /* URL? */
+ NULL, /* encoding */
+ XML_PARSE_RECOVER | XML_PARSE_NOCDATA);
+ nodes = g_queue_new ();
+
+ while ((res = xmlTextReaderRead (priv->reader)) == 1)
+ {
+ switch (xmlTextReaderNodeType (priv->reader))
+ {
+ case XML_READER_TYPE_ELEMENT:
+ /* Lookup the "name" for the tag */
+ name = xmlTextReaderConstLocalName (priv->reader);
+ g_debug (G_STRLOC ": Opening tag: %s", name);
+
+ /* Create our new node for this tag */
+
+ new_node = rest_xml_node_new ();
+ new_node->name = g_strdup (name);
+
+ if (!root_node)
+ {
+ root_node = new_node;
+ }
+
+ /*
+ * Check if we are not the root node because we need to update it's
+ * children set to include the new node.
+ */
+ if (cur_node)
+ {
+ tmp_node = g_hash_table_lookup (cur_node->children, name);
+
+ if (tmp_node)
+ {
+ g_debug (G_STRLOC ": Existing node found for this name. "
+ "Prepending to the list.");
+ g_hash_table_insert (cur_node->children,
+ (gchar *)new_node->name,
+ rest_xml_node_prepend (tmp_node, new_node));
+ } else {
+ g_debug (G_STRLOC ": Unseen name. Adding to the children table.");
+ g_hash_table_insert (cur_node->children,
+ (gchar *)new_node->name,
+ new_node);
+ }
+ }
+
+ /*
+ * Check for empty element. If empty we needn't worry about children
+ * or text and thus we don't need to update the stack or state
+ */
+ if (xmlTextReaderIsEmptyElement (priv->reader))
+ {
+ g_debug (G_STRLOC ": We have an empty element. No children or text.");
+ } else {
+ g_debug (G_STRLOC ": Non-empty element found."
+ " Pushing to stack and updating current state.");
+ g_queue_push_head (nodes, new_node);
+ cur_node = new_node;
+ }
+
+ /*
+ * Check if we have attributes. These get stored in the node's attrs
+ * hash table.
+ */
+ if (xmlTextReaderHasAttributes (priv->reader))
+ {
+ xmlTextReaderMoveToFirstAttribute (priv->reader);
+
+ do {
+ attr_name = xmlTextReaderConstLocalName (priv->reader);
+ attr_value = xmlTextReaderConstValue (priv->reader);
+ g_hash_table_insert (new_node->attrs,
+ g_strdup (attr_name),
+ g_strdup (attr_value));
+
+ g_debug (G_STRLOC ": Attribute found: %s = %s",
+ attr_name,
+ attr_value);
+
+ } while ((res = xmlTextReaderMoveToNextAttribute (priv->reader)) == 1);
+ }
+
+ break;
+ case XML_READER_TYPE_END_ELEMENT:
+ g_debug (G_STRLOC ": Closing tag: %s",
+ xmlTextReaderConstLocalName (priv->reader));
+
+ g_debug (G_STRLOC ": Popping from stack and updating state.");
+ g_queue_pop_head (nodes);
+ cur_node = (RestXmlNode *)g_queue_peek_head (nodes);
+
+ if (cur_node)
+ {
+ g_debug (G_STRLOC ": Head is now %s", cur_node->name);
+ } else {
+ g_debug (G_STRLOC ": At the top level");
+ }
+ break;
+ case XML_READER_TYPE_TEXT:
+ cur_node->content = g_strdup (xmlTextReaderConstValue (priv->reader));
+ g_debug (G_STRLOC ": Text content found: %s",
+ cur_node->content);
+ default:
+ g_debug (G_STRLOC ": Found unknown content with type: 0x%x",
+ xmlTextReaderNodeType (priv->reader));
+ break;
+ }
+ }
+
+ xmlTextReaderClose (priv->reader);
+ return root_node;
+}
+
+
diff --git a/rest/rest-xml-parser.h b/rest/rest-xml-parser.h
new file mode 100644
index 0000000..060579f
--- /dev/null
+++ b/rest/rest-xml-parser.h
@@ -0,0 +1,54 @@
+#ifndef _REST_XML_PARSER
+#define _REST_XML_PARSER
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define REST_TYPE_XML_PARSER rest_xml_parser_get_type()
+
+#define REST_XML_PARSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), REST_TYPE_XML_PARSER, RestXmlParser))
+
+#define REST_XML_PARSER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), REST_TYPE_XML_PARSER, RestXmlParserClass))
+
+#define REST_IS_XML_PARSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), REST_TYPE_XML_PARSER))
+
+#define REST_IS_XML_PARSER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), REST_TYPE_XML_PARSER))
+
+#define REST_XML_PARSER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), REST_TYPE_XML_PARSER, RestXmlParserClass))
+
+typedef struct {
+ GObject parent;
+} RestXmlParser;
+
+typedef struct {
+ GObjectClass parent_class;
+} RestXmlParserClass;
+
+typedef struct _RestXmlNode RestXmlNode;
+struct _RestXmlNode {
+ gchar *name;
+ gchar *content;
+ GHashTable *children;
+ GHashTable *attrs;
+ RestXmlNode *next;
+};
+
+GType rest_xml_parser_get_type (void);
+
+RestXmlNode *rest_xml_node_new (void);
+void rest_xml_node_free (RestXmlNode *node);
+
+RestXmlParser *rest_xml_parser_new (void);
+RestXmlNode *rest_xml_parser_parse_from_data (RestXmlParser *parser,
+ const gchar *data,
+ gssize len);
+
+G_END_DECLS
+
+#endif /* _REST_XML_PARSER */