diff options
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | examples/Makefile.am | 3 | ||||
-rw-r--r-- | examples/test-xml.c | 123 | ||||
-rw-r--r-- | rest/Makefile.am | 8 | ||||
-rw-r--r-- | rest/rest-xml-parser.c | 250 | ||||
-rw-r--r-- | rest/rest-xml-parser.h | 54 |
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 */ |