[gnome-builder/wip/chergert/jsonrpc-gvariant] jsonrpc: port jsonrpc to use GVariant instead of JsonNode



commit b9ff1dc42c4ed19155ad7916c8c1a72f3cb018ee
Author: Christian Hergert <chergert redhat com>
Date:   Fri Mar 17 13:38:06 2017 -0700

    jsonrpc: port jsonrpc to use GVariant instead of JsonNode
    
    By switching all of this API to use GVariant, we allow ourselves to be
    able to upgrade the connection to something more efficient than Json.
    
    The problem with Json is that it requires creating a great deal of small
    objects at parse time which can drastically fragment memory. For example,
    if we get 1000 auto-completion items from a worker, we would like to be
    able to keep all of those results in a single contiguous buffer and access
    them (and their string data) by pointing into the buffer.
    
    Doing so will allow us to make our completion engine much lighter weight,
    with the exception of GtkSourceView's requirement on GList+GObjects being
    pre-inflated. (Although we'll likely switch to our own completion system
    at some point in the future to work around all this and get other desired
    features).

 configure.ac                                       |    3 +-
 contrib/jsonrpc-glib/Makefile.am                   |   23 +-
 contrib/jsonrpc-glib/jcon.c                        |  670 --------------------
 contrib/jsonrpc-glib/jcon.h                        |  130 ----
 contrib/jsonrpc-glib/jsonrpc-client.c              |  538 ++++++++--------
 contrib/jsonrpc-glib/jsonrpc-client.h              |   30 +-
 contrib/jsonrpc-glib/jsonrpc-glib.h                |    2 +-
 .../jsonrpc-glib/jsonrpc-input-stream-private.h    |   30 +
 contrib/jsonrpc-glib/jsonrpc-input-stream.c        |  136 +++--
 contrib/jsonrpc-glib/jsonrpc-input-stream.h        |    5 +-
 contrib/jsonrpc-glib/jsonrpc-message.c             |  475 ++++++++++++++
 contrib/jsonrpc-glib/jsonrpc-message.h             |  171 +++++
 contrib/jsonrpc-glib/jsonrpc-output-stream.c       |  191 +++++-
 contrib/jsonrpc-glib/jsonrpc-output-stream.h       |    7 +-
 contrib/jsonrpc-glib/jsonrpc-server.c              |    6 +-
 contrib/jsonrpc-glib/jsonrpc-server.h              |    7 +-
 contrib/jsonrpc-glib/jsonrpc-version.h.in          |    2 +-
 contrib/jsonrpc-glib/test-message.c                |  162 +++++
 contrib/jsonrpc-glib/test-server.c                 |  179 ++++++
 libide/langserv/ide-langserv-client.c              |  152 +++---
 libide/langserv/ide-langserv-client.h              |   56 +-
 libide/langserv/ide-langserv-completion-provider.c |   76 ++--
 libide/langserv/ide-langserv-highlighter.c         |   34 +-
 libide/langserv/ide-langserv-rename-provider.c     |   56 +-
 libide/langserv/ide-langserv-symbol-resolver.c     |   99 ++--
 plugins/flatpak/gbp-flatpak-clone-widget.c         |    1 +
 plugins/flatpak/gbp-flatpak-configuration.c        |    2 +
 src/main.c                                         |    1 +
 tests/Makefile.am                                  |   17 -
 tests/test-jcon.c                                  |  166 -----
 30 files changed, 1824 insertions(+), 1603 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 30c4c3d..bae6bda 100644
--- a/configure.ac
+++ b/configure.ac
@@ -217,7 +217,8 @@ PKG_CHECK_MODULES(GSTYLE,   [glib-2.0 >= glib_required_version
                              gtk+-3.0 >= gtk_required_version
                              gtksourceview-3.0 >= gtksourceview_required_version])
 PKG_CHECK_MODULES(ICONS,    [gio-2.0 >= glib_required_version])
-PKG_CHECK_MODULES(JSONRPC,  [json-glib-1.0 >= json_glib_required_version])
+PKG_CHECK_MODULES(JSONRPC,  [json-glib-1.0 >= json_glib_required_version
+                             gio-unix-2.0])
 PKG_CHECK_MODULES(LIBIDE,   [gio-2.0 >= glib_required_version
                              gio-unix-2.0 >= glib_required_version
                              gtk+-3.0 >= gtk_required_version
diff --git a/contrib/jsonrpc-glib/Makefile.am b/contrib/jsonrpc-glib/Makefile.am
index a45dcb2..5b5c28b 100644
--- a/contrib/jsonrpc-glib/Makefile.am
+++ b/contrib/jsonrpc-glib/Makefile.am
@@ -19,8 +19,8 @@ libjsonrpc_glib_la_public_sources =          \
 libjsonrpc_glib_la_SOURCES =                 \
        $(libjsonrpc_glib_la_public_sources) \
        jsonrpc-glib.h                       \
-       jcon.c                               \
-       jcon.h                               \
+       jsonrpc-message.c                    \
+       jsonrpc-message.h                    \
        $(NULL)
 
 libjsonrpc_glib_la_CFLAGS =                  \
@@ -82,4 +82,23 @@ EXTRA_DIST += jsonrpc-glib.deps
 DISTCLEANFILES += $(vapi_DATA)
 endif
 
+noinst_PROGRAMS =
+TESTS =
+
+noinst_PROGRAMS += test-message
+TESTS += test-message
+test_message_CFLAGS = $(JSONRPC_CFLAGS)
+test_message_LDADD = \
+       $(JSONRPC_LIBS) \
+       libjsonrpc-glib.la \
+       $(NULL)
+
+noinst_PROGRAMS += test-server
+TESTS += test-server
+test_server_CFLAGS = $(JSONRPC_CFLAGS)
+test_server_LDADD = \
+       $(JSONRPC_LIBS) \
+       libjsonrpc-glib.la \
+       $(NULL)
+
 -include $(top_srcdir)/git.mk
diff --git a/contrib/jsonrpc-glib/jsonrpc-client.c b/contrib/jsonrpc-glib/jsonrpc-client.c
index 7986089..3e58e46 100644
--- a/contrib/jsonrpc-glib/jsonrpc-client.c
+++ b/contrib/jsonrpc-glib/jsonrpc-client.c
@@ -19,7 +19,7 @@
 #define G_LOG_DOMAIN "jsonrpc-client"
 
 /**
- * SECTION:jsonrpcclient:
+ * SECTION:jsonrpc-client:
  * @title: JsonrpcClient
  * @short_description: a client for JSON-RPC communication
  *
@@ -39,7 +39,7 @@
  *
  * To make an RPC call, use jsonrpc_client_call() or
  * jsonrpc_client_call_async() and provide the method name and the parameters
- * as a #JsonNode for call.
+ * as a #GVariant for call.
  *
  * It is a programming error to mix synchronous and asynchronous API calls
  * of the #JsonrpcClient class.
@@ -51,9 +51,9 @@
 
 #include <glib.h>
 
-#include "jcon.h"
 #include "jsonrpc-client.h"
 #include "jsonrpc-input-stream.h"
+#include "jsonrpc-input-stream-private.h"
 #include "jsonrpc-output-stream.h"
 
 typedef struct
@@ -78,14 +78,14 @@ typedef struct
   /*
    * The input_stream field contains our wrapper input stream around the
    * underlying input stream provided by JsonrpcClient::io-stream. This
-   * allows us to conveniently write JsonNode instances.
+   * allows us to conveniently write GVariant  instances.
    */
   JsonrpcInputStream *input_stream;
 
   /*
    * The output_stream field contains our wrapper output stream around the
    * underlying output stream provided by JsonrpcClient::io-stream. This
-   * allows us to convieniently read JsonNode instances.
+   * allows us to convieniently read GVariant instances.
    */
   JsonrpcOutputStream *output_stream;
 
@@ -101,7 +101,7 @@ typedef struct
    * Every JSONRPC invocation needs a request id. This is a monotonic
    * integer that we encode as a string to the server.
    */
-  gint sequence;
+  gint64 sequence;
 
   /*
    * This bit indicates if we have sent a call yet. Once we send our
@@ -122,6 +122,13 @@ typedef struct
    * circuit on future operations sooner.
    */
   guint failed : 1;
+
+  /*
+   * If we should try to use gvariant encoding when communicating with
+   * our peer. This is helpful to be able to lower parser and memory
+   * overhead.
+   */
+  guint use_gvariant : 1;
 } JsonrpcClientPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (JsonrpcClient, jsonrpc_client, G_TYPE_OBJECT)
@@ -129,6 +136,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (JsonrpcClient, jsonrpc_client, G_TYPE_OBJECT)
 enum {
   PROP_0,
   PROP_IO_STREAM,
+  PROP_USE_GVARIANT,
   N_PROPS
 };
 
@@ -145,55 +153,43 @@ static guint signals [N_SIGNALS];
  * Check to see if this looks like a jsonrpc 2.0 reply of any kind.
  */
 static gboolean
-is_jsonrpc_reply (JsonNode *node)
+is_jsonrpc_reply (GVariantDict *dict)
 {
-  JsonObject *object;
-  const gchar *value;
-
-  return JSON_NODE_HOLDS_OBJECT (node) &&
-         NULL != (object = json_node_get_object (node)) &&
-         json_object_has_member (object, "jsonrpc") &&
-         NULL != (value = json_object_get_string_member (object, "jsonrpc")) &&
-         (g_strcmp0 (value, "2.0") == 0);
+  const gchar *value = NULL;
+
+  g_assert (dict != NULL);
+
+  return (g_variant_dict_contains (dict, "jsonrpc") &&
+          g_variant_dict_lookup (dict, "jsonrpc", "&s", &value) &&
+          g_str_equal (value, "2.0"));
 }
 
 /*
  * Check to see if this looks like a notification reply.
  */
 static gboolean
-is_jsonrpc_notification (JsonNode *node)
+is_jsonrpc_notification (GVariantDict *dict)
 {
-  JsonObject *object;
-  const gchar *value;
-
-  g_assert (JSON_NODE_HOLDS_OBJECT (node));
+  const gchar *method = NULL;
 
-  object = json_node_get_object (node);
+  g_assert (dict != NULL);
 
-  return !json_object_has_member (object, "id") &&
-         json_object_has_member (object, "method") &&
-         NULL != (value = json_object_get_string_member (object, "method")) &&
-         value != NULL && *value != '\0';
+  return (!g_variant_dict_contains (dict, "id") &&
+          g_variant_dict_contains (dict, "method") &&
+          g_variant_dict_lookup (dict, "method", "&s", &method) &&
+          method != NULL && *method != '\0');
 }
 
 /*
  * Check to see if this looks like a proper result for an RPC.
  */
 static gboolean
-is_jsonrpc_result (JsonNode *node)
+is_jsonrpc_result (GVariantDict *dict)
 {
-  JsonObject *object;
-  JsonNode *field;
-
-  g_assert (JSON_NODE_HOLDS_OBJECT (node));
+  g_assert (dict != NULL);
 
-  object = json_node_get_object (node);
-
-  return json_object_has_member (object, "id") &&
-         NULL != (field = json_object_get_member (object, "id")) &&
-         JSON_NODE_HOLDS_VALUE (field) &&
-         json_node_get_int (field) > 0 &&
-         json_object_has_member (object, "result");
+  return (g_variant_dict_contains (dict, "id") &&
+          g_variant_dict_contains (dict, "result"));
 }
 
 
@@ -201,91 +197,16 @@ is_jsonrpc_result (JsonNode *node)
  * Check to see if this looks like a proper method call for an RPC.
  */
 static gboolean
-is_jsonrpc_call (JsonNode     *node,
-                 JsonNode    **id,
-                 JsonNode    **params,
-                 const gchar **method)
+is_jsonrpc_call (GVariantDict *dict)
 {
-  JsonNode *tmp_id = NULL;
-  JsonNode *tmp_params = NULL;
-  const gchar *tmp_method = NULL;
-  gboolean success;
-
-  g_assert (JSON_NODE_HOLDS_OBJECT (node));
-
-  success = JCON_EXTRACT (node,
-    "id", JCONE_NODE (tmp_id),
-    "method", JCONE_STRING (tmp_method),
-    "params", JCONE_NODE (tmp_params)
-  );
-
-  if (success && tmp_id != NULL && tmp_method != NULL && tmp_params != NULL)
-    {
-      if (id != NULL)
-        *id = tmp_id;
-
-      if (method != NULL)
-        *method = tmp_method;
-
-      if (params != NULL)
-        *params = tmp_params;
-
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-/*
- * Try to unwrap the error and possibly set @id to the extracted RPC
- * request id.
- */
-static gboolean
-unwrap_jsonrpc_error (JsonNode  *node,
-                      gint      *id,
-                      GError   **error)
-{
-  JsonObject *object;
-  JsonObject *err_obj;
-  JsonNode *field;
-
-  g_assert (node != NULL);
-  g_assert (id != NULL);
-  g_assert (error != NULL);
-
-  if (!JSON_NODE_HOLDS_OBJECT (node))
-    return FALSE;
-
-  object = json_node_get_object (node);
-
-  if (json_object_has_member (object, "id") &&
-      NULL != (field = json_object_get_member (object, "id")) &&
-      JSON_NODE_HOLDS_VALUE (field) &&
-      json_node_get_int (field) > 0)
-    *id = json_node_get_int (field);
-  else
-    *id = -1;
-
-  if (json_object_has_member (object, "error") &&
-      NULL != (field = json_object_get_member (object, "error")) &&
-      JSON_NODE_HOLDS_OBJECT (field) &&
-      NULL != (err_obj = json_node_get_object (field)))
-    {
-      const gchar *message;
-      gint code;
-
-      message = json_object_get_string_member (err_obj, "message");
-      code = json_object_get_int_member (err_obj, "code");
-
-      if (message == NULL || *message == '\0')
-        message = "Unknown error occurred";
-
-      g_set_error_literal (error, JSONRPC_CLIENT_ERROR, code, message);
+  const gchar *method = NULL;
 
-      return TRUE;
-    }
+  g_assert (dict != NULL);
 
-  return FALSE;
+  return (g_variant_dict_contains (dict, "id") &&
+          g_variant_dict_contains (dict, "method") &&
+          g_variant_dict_lookup (dict, "method", "&s", &method) &&
+          g_variant_dict_contains (dict, "params"));
 }
 
 /*
@@ -300,18 +221,15 @@ jsonrpc_client_panic (JsonrpcClient *self,
 {
   JsonrpcClientPrivate *priv = jsonrpc_client_get_instance_private (self);
   g_autoptr(GHashTable) invocations = NULL;
-  g_autoptr(JsonrpcClient) hold = NULL;
   GHashTableIter iter;
   GTask *task;
 
   g_assert (JSONRPC_IS_CLIENT (self));
   g_assert (error != NULL);
 
-  hold = g_object_ref (self);
-
   priv->failed = TRUE;
 
-  g_warning ("%s", error->message);
+  g_warning ("%s(): %s", G_STRFUNC, error->message);
 
   jsonrpc_client_close (self, NULL, NULL);
 
@@ -404,6 +322,25 @@ jsonrpc_client_finalize (GObject *object)
 }
 
 static void
+jsonrpc_client_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  JsonrpcClient *self = JSONRPC_CLIENT (object);
+
+  switch (prop_id)
+    {
+    case PROP_USE_GVARIANT:
+      g_value_set_boolean (value, jsonrpc_client_get_use_gvariant (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
 jsonrpc_client_set_property (GObject      *object,
                              guint         prop_id,
                              const GValue *value,
@@ -418,6 +355,10 @@ jsonrpc_client_set_property (GObject      *object,
       priv->io_stream = g_value_dup_object (value);
       break;
 
+    case PROP_USE_GVARIANT:
+      jsonrpc_client_set_use_gvariant (self, g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -430,6 +371,7 @@ jsonrpc_client_class_init (JsonrpcClientClass *klass)
 
   object_class->constructed = jsonrpc_client_constructed;
   object_class->finalize = jsonrpc_client_finalize;
+  object_class->get_property = jsonrpc_client_get_property;
   object_class->set_property = jsonrpc_client_set_property;
 
   properties [PROP_IO_STREAM] =
@@ -439,14 +381,21 @@ jsonrpc_client_class_init (JsonrpcClientClass *klass)
                          G_TYPE_IO_STREAM,
                          (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
+  properties [PROP_USE_GVARIANT] =
+    g_param_spec_boolean ("use-gvariant",
+                          "Use GVariant",
+                          "If GVariant encoding should be used",
+                          FALSE,
+                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
   /**
    * JsonrpcClient::handle-call:
    * @self: A #JsonrpcClient
-   * @method: the method name
-   * @id: The "id" field of the JSONRPC message
-   * @params: The "params" field of the JSONRPC message
+   * @method: (not nullable): the method name
+   * @id: (not nullable): The "id" field of the JSONRPC message
+   * @params: (nullable): The "params" field of the JSONRPC message
    *
    * This signal is emitted when an RPC has been received from
    * the peer we are connected to. Return %TRUE if you have handled
@@ -466,14 +415,14 @@ jsonrpc_client_class_init (JsonrpcClientClass *klass)
                   G_TYPE_BOOLEAN,
                   3,
                   G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
-                  JSON_TYPE_NODE,
-                  JSON_TYPE_NODE);
+                  G_TYPE_VARIANT,
+                  G_TYPE_VARIANT);
 
   /**
    * JsonrpcClient::notification:
    * @self: A #JsonrpcClient
    * @method: the method name of the notification
-   * @params: params for the notification
+   * @params: (nullable): params for the notification
    *
    * This signal is emitted when a notification has been received
    * from a peer. Unlike #JsonrpcClient::handle-call, this does
@@ -489,7 +438,7 @@ jsonrpc_client_class_init (JsonrpcClientClass *klass)
                   G_TYPE_NONE,
                   2,
                   G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
-                  JSON_TYPE_NODE);
+                  G_TYPE_VARIANT);
 }
 
 static void
@@ -576,17 +525,14 @@ jsonrpc_client_call_read_cb (GObject      *object,
   JsonrpcInputStream *stream = (JsonrpcInputStream *)object;
   g_autoptr(JsonrpcClient) self = user_data;
   JsonrpcClientPrivate *priv = jsonrpc_client_get_instance_private (self);
-  g_autoptr(JsonNode) node = NULL;
+  g_autoptr(GVariant) message = NULL;
   g_autoptr(GError) error = NULL;
-  JsonNode *id_node = NULL;
-  JsonNode *params_node = NULL;
-  const gchar *method = NULL;
-  gint id = -1;
+  g_auto(GVariantDict) dict = { 0 };
 
   g_assert (JSONRPC_IS_INPUT_STREAM (stream));
   g_assert (JSONRPC_IS_CLIENT (self));
 
-  if (!jsonrpc_input_stream_read_message_finish (stream, result, &node, &error))
+  if (!jsonrpc_input_stream_read_message_finish (stream, result, &message, &error))
     {
       /*
        * Handle jsonrpc_client_close() conditions gracefully.
@@ -603,14 +549,27 @@ jsonrpc_client_call_read_cb (GObject      *object,
       return;
     }
 
-  g_assert (node != NULL);
+  g_assert (message != NULL);
+
+  /* If we received a gvariant-based message, upgrade connection */
+  if (_jsonrpc_input_stream_get_has_seen_gvariant (stream))
+    jsonrpc_client_set_use_gvariant (self, TRUE);
+
+  /* Make sure we got a proper type back from the variant. */
+  if (!g_variant_is_of_type (message, G_VARIANT_TYPE_VARDICT))
+    {
+      jsonrpc_client_panic (self, error);
+      return;
+    }
+
+  g_variant_dict_init (&dict, message);
 
   /*
    * If the message is malformed, we'll also need to perform another read.
    * We do this to try to be relaxed against failures. That seems to be
    * the JSONRPC way, although I'm not sure I like the idea.
    */
-  if (!is_jsonrpc_reply (node))
+  if (!is_jsonrpc_reply (&dict))
     {
       error = g_error_new_literal (G_IO_ERROR,
                                    G_IO_ERROR_INVALID_DATA,
@@ -623,78 +582,86 @@ jsonrpc_client_call_read_cb (GObject      *object,
    * If the response does not have an "id" field, then it is a "notification"
    * and we need to emit the "notificiation" signal.
    */
-  if (is_jsonrpc_notification (node))
+  if (is_jsonrpc_notification (&dict))
     {
-      g_autoptr(JsonNode) empty_params = NULL;
-      const gchar *method_name;
-      JsonObject *obj;
-      JsonNode *params;
-
-      obj = json_node_get_object (node);
-      method_name = json_object_get_string_member (obj, "method");
-      params = json_object_get_member (obj, "params");
+      g_autoptr(GVariant) params = NULL;
+      const gchar *method_name = NULL;
 
-      if (params == NULL)
-        params = empty_params = json_node_new (JSON_NODE_ARRAY);
-
-      g_signal_emit (self, signals [NOTIFICATION], 0, method_name, params);
+      if (g_variant_dict_lookup (&dict, "method", "&s", &method_name))
+        {
+          params = g_variant_dict_lookup_value (&dict, "params", NULL);
+          g_signal_emit (self, signals [NOTIFICATION], 0, method_name, params);
+        }
 
       goto begin_next_read;
     }
 
-  if (is_jsonrpc_result (node))
+  if (is_jsonrpc_result (&dict))
     {
-      JsonObject *obj;
-      JsonNode *res;
+      g_autoptr(GVariant) params = NULL;
+      gint64 id = -1;
       GTask *task;
 
-      obj = json_node_get_object (node);
-      id = json_object_get_int_member (obj, "id");
-      res = json_object_get_member (obj, "result");
-
-      task = g_hash_table_lookup (priv->invocations, GINT_TO_POINTER (id));
-
-      if (task != NULL)
+      if (!g_variant_dict_lookup (&dict, "id", "x", &id) ||
+          NULL == (task = g_hash_table_lookup (priv->invocations, GINT_TO_POINTER (id))))
         {
-          g_task_return_pointer (task, json_node_copy (res), (GDestroyNotify)json_node_unref);
-          goto begin_next_read;
+          error = g_error_new_literal (G_IO_ERROR,
+                                       G_IO_ERROR_INVALID_DATA,
+                                       "Reply to missing or invalid task");
+          jsonrpc_client_panic (self, error);
+          return;
         }
 
-      error = g_error_new_literal (G_IO_ERROR,
-                                   G_IO_ERROR_INVALID_DATA,
-                                   "Reply to missing or invalid task");
-      jsonrpc_client_panic (self, error);
-      return;
+      if (NULL != (params = g_variant_dict_lookup_value (&dict, "result", NULL)))
+        g_task_return_pointer (task, g_steal_pointer (&params), (GDestroyNotify)g_variant_unref);
+      else
+        g_task_return_pointer (task, NULL, NULL);
+
+      goto begin_next_read;
     }
 
   /*
    * If this is a method call, emit the handle-call signal.
    */
-  if (is_jsonrpc_call (node,
-                       &id_node,
-                       &params_node,
-                       &method))
+  if (is_jsonrpc_call (&dict))
     {
+      g_autoptr(GVariant) id = NULL;
+      g_autoptr(GVariant) params = NULL;
+      const gchar *method_name = NULL;
       gboolean ret = FALSE;
 
-      g_signal_emit (self, signals [HANDLE_CALL], 0, method, id_node, params_node, &ret);
+      if (!g_variant_dict_lookup (&dict, "method", "&s", &method_name) ||
+          NULL == (id = g_variant_dict_lookup_value (&dict, "id", NULL)))
+        {
+          error = g_error_new (G_IO_ERROR,
+                               G_IO_ERROR_INVALID_DATA,
+                               "Call contains invalid method or id field");
+          jsonrpc_client_panic (self, error);
+          return;
+        }
+
+      params = g_variant_dict_lookup_value (&dict, "params", NULL);
+
+      g_assert (method_name != NULL);
+      g_assert (id != NULL);
+
+      g_signal_emit (self, signals [HANDLE_CALL], 0, method_name, id, params, &ret);
 
       if (ret == FALSE)
         {
-          g_autoptr(JsonNode) reply = NULL;
-
-          reply = JCON_NEW (
-            "jsonrpc", "2.0",
-            "id", JCON_NODE (id_node),
-            "error", "{",
-              "code", JCON_INT (-32601),
-              "message", "The method does not exist or is not available",
-            "}"
-          );
-
-          jsonrpc_output_stream_write_message_async (priv->output_stream,
-                                                     g_steal_pointer (&reply),
-                                                     NULL, NULL, NULL);
+          GVariantDict reply;
+          GVariantDict error_dict;
+
+          g_variant_dict_init (&error_dict, NULL);
+          g_variant_dict_insert (&error_dict, "code", "i", -32601);
+          g_variant_dict_insert (&error_dict, "message", "s", "The method does not exist or is not 
available");
+
+          g_variant_dict_init (&reply, NULL);
+          g_variant_dict_insert (&reply, "jsonrpc", "s", "2.0");
+          g_variant_dict_insert_value (&reply, "id", id);
+          g_variant_dict_insert_value (&reply, "error", g_variant_dict_end (&error_dict));
+
+          jsonrpc_output_stream_write_message_async (priv->output_stream, g_variant_dict_end (&reply), NULL, 
NULL, NULL);
         }
 
       return;
@@ -704,17 +671,29 @@ jsonrpc_client_call_read_cb (GObject      *object,
    * If we got an error destined for one of our inflight invocations, then
    * we need to dispatch it now.
    */
-  if (unwrap_jsonrpc_error (node, &id, &error))
+
+  if (g_variant_dict_contains (&dict, "id") &&
+      g_variant_dict_contains (&dict, "error"))
     {
-      if (id > 0)
+      g_auto(GVariantDict) error_dict = { 0 };
+      g_autoptr(GVariant) error_variant = NULL;
+      g_autofree gchar *errstr = NULL;
+      gint64 id = -1;
+
+      error_variant = g_variant_dict_lookup_value (&dict, "error", NULL);
+      errstr = g_variant_print (error_variant, FALSE);
+      error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, errstr);
+
+      if (g_variant_dict_lookup (&dict, "id", "x", &id))
         {
           GTask *task = g_hash_table_lookup (priv->invocations, GINT_TO_POINTER (id));
 
           if (task != NULL)
-            {
-              g_task_return_error (task, g_steal_pointer (&error));
-              goto begin_next_read;
-            }
+            g_task_return_error (task, g_steal_pointer (&error));
+          else
+            g_warning ("Received error for task %"G_GINT64_FORMAT" which is unknown", id);
+
+          goto begin_next_read;
         }
 
       /*
@@ -725,10 +704,7 @@ jsonrpc_client_call_read_cb (GObject      *object,
       return;
     }
 
-  {
-    g_autofree gchar *str = json_to_string (node, FALSE);
-    g_warning ("Unhandled message: %s", str);
-  }
+  g_warning ("Unhandled RPC from peer!");
 
 begin_next_read:
   if (priv->input_stream != NULL && priv->in_shutdown == FALSE)
@@ -744,8 +720,8 @@ jsonrpc_client_call_sync_cb (GObject      *object,
                              gpointer      user_data)
 {
   JsonrpcClient *self = (JsonrpcClient *)object;
-  g_autoptr(GTask) task = user_data;
-  g_autoptr(JsonNode) return_value = NULL;
+  GTask *task = user_data;
+  g_autoptr(GVariant) return_value = NULL;
   g_autoptr(GError) error = NULL;
 
   g_assert (JSONRPC_IS_CLIENT (self));
@@ -755,38 +731,38 @@ jsonrpc_client_call_sync_cb (GObject      *object,
   if (!jsonrpc_client_call_finish (self, result, &return_value, &error))
     g_task_return_error (task, g_steal_pointer (&error));
   else
-    g_task_return_pointer (task, g_steal_pointer (&return_value), (GDestroyNotify)json_node_unref);
+    g_task_return_pointer (task, g_steal_pointer (&return_value), (GDestroyNotify)g_variant_unref);
 }
 
 /**
  * jsonrpc_client_call:
  * @self: A #JsonrpcClient
  * @method: the name of the method to call
- * @params: (transfer full) (nullable): A #JsonNode of parameters or %NULL
+ * @params: (transfer none) (nullable): A #GVariant of parameters or %NULL
  * @cancellable: (nullable): A #GCancellable or %NULL
  * @return_value: (nullable) (out): A location for a #JsonNode.
  *
  * Synchronously calls @method with @params on the remote peer.
  *
- * This function takes ownership of @params.
- *
  * once a reply has been received, or failure, this function will return.
  * If successful, @return_value will be set with the reslut field of
  * the response.
  *
+ * If @params is floating then this function consumes the reference.
+ *
  * Returns; %TRUE on success; otherwise %FALSE and @error is set.
  */
 gboolean
 jsonrpc_client_call (JsonrpcClient  *self,
                      const gchar    *method,
-                     JsonNode       *params,
+                     GVariant       *params,
                      GCancellable   *cancellable,
-                     JsonNode      **return_value,
+                     GVariant      **return_value,
                      GError        **error)
 {
   g_autoptr(GTask) task = NULL;
   g_autoptr(GMainContext) main_context = NULL;
-  g_autoptr(JsonNode) local_return_value = NULL;
+  g_autoptr(GVariant) local_return_value = NULL;
   gboolean ret;
 
   g_return_val_if_fail (JSONRPC_IS_CLIENT (self), FALSE);
@@ -803,7 +779,7 @@ jsonrpc_client_call (JsonrpcClient  *self,
                              params,
                              cancellable,
                              jsonrpc_client_call_sync_cb,
-                             g_object_ref (task));
+                             task);
 
   while (!g_task_get_completed (task))
     g_main_context_iteration (main_context, TRUE);
@@ -821,7 +797,7 @@ jsonrpc_client_call (JsonrpcClient  *self,
  * jsonrpc_client_call_async:
  * @self: A #JsonrpcClient
  * @method: the name of the method to call
- * @params: (transfer full) (nullable): A #JsonNode of parameters or %NULL
+ * @params: (transfer none) (nullable): A #JsonNode of parameters or %NULL
  * @cancellable: (nullable): A #GCancellable or %NULL
  * @callback: a callback to executed upon completion
  * @user_data: user data for @callback
@@ -837,16 +813,17 @@ jsonrpc_client_call (JsonrpcClient  *self,
 void
 jsonrpc_client_call_async (JsonrpcClient       *self,
                            const gchar         *method,
-                           JsonNode            *params,
+                           GVariant            *params,
                            GCancellable        *cancellable,
                            GAsyncReadyCallback  callback,
                            gpointer             user_data)
 {
   JsonrpcClientPrivate *priv = jsonrpc_client_get_instance_private (self);
-  g_autoptr(JsonNode) message = NULL;
+  g_autoptr(GVariant) message = NULL;
   g_autoptr(GTask) task = NULL;
   g_autoptr(GError) error = NULL;
-  gint id;
+  GVariantDict dict;
+  gint64 id;
 
   g_return_if_fail (JSONRPC_IS_CLIENT (self));
   g_return_if_fail (method != NULL);
@@ -870,15 +847,17 @@ jsonrpc_client_call_async (JsonrpcClient       *self,
 
   g_task_set_task_data (task, GINT_TO_POINTER (id), NULL);
 
+  /* Use empty maybe type for NULL, and floating reference will
+   * be consumed by g_variant_dict_insert_value() below. */
   if (params == NULL)
-    params = json_node_new (JSON_NODE_NULL);
+    params = g_variant_new_maybe (G_VARIANT_TYPE_VARIANT, NULL);
 
-  message = JCON_NEW (
-    "jsonrpc", "2.0",
-    "id", JCON_INT (id),
-    "method", JCON_STRING (method),
-    "params", JCON_NODE (params)
-  );
+  g_variant_dict_init (&dict, NULL);
+  g_variant_dict_insert (&dict, "jsonrpc", "s", "2.0");
+  g_variant_dict_insert (&dict, "id", "x", id);
+  g_variant_dict_insert (&dict, "method", "s", method);
+  g_variant_dict_insert_value (&dict, "params", params);
+  message = g_variant_dict_end (&dict);
 
   g_hash_table_insert (priv->invocations, GINT_TO_POINTER (id), g_object_ref (task));
 
@@ -906,7 +885,7 @@ jsonrpc_client_call_async (JsonrpcClient       *self,
 gboolean
 jsonrpc_client_call_finish (JsonrpcClient  *self,
                             GAsyncResult   *result,
-                            JsonNode      **return_value,
+                            GVariant      **return_value,
                             GError        **error)
 {
   g_autoptr(JsonNode) local_return_value = NULL;
@@ -966,12 +945,13 @@ jsonrpc_client_send_notification_write_cb (GObject      *object,
 gboolean
 jsonrpc_client_send_notification (JsonrpcClient  *self,
                                   const gchar    *method,
-                                  JsonNode       *params,
+                                  GVariant       *params,
                                   GCancellable   *cancellable,
                                   GError        **error)
 {
   JsonrpcClientPrivate *priv = jsonrpc_client_get_instance_private (self);
-  g_autoptr(JsonNode) message = NULL;
+  g_autoptr(GVariant) message = NULL;
+  GVariantDict dict;
   gboolean ret;
 
   g_return_val_if_fail (JSONRPC_IS_CLIENT (self), FALSE);
@@ -981,19 +961,19 @@ jsonrpc_client_send_notification (JsonrpcClient  *self,
   if (!jsonrpc_client_check_ready (self, error))
     return FALSE;
 
+  /* Use empty maybe type for NULL params. The floating reference will
+   * be consumed below in g_variant_dict_insert_value(). */
   if (params == NULL)
-    params = json_node_new (JSON_NODE_NULL);
+    params = g_variant_new_maybe (G_VARIANT_TYPE_VARIANT, NULL);
 
-  message = JCON_NEW (
-    "jsonrpc", "2.0",
-    "method", JCON_STRING (method),
-    "params", JCON_NODE (params)
-  );
+  g_variant_dict_init (&dict, NULL);
+  g_variant_dict_insert (&dict, "jsonrpc", "s", "2.0");
+  g_variant_dict_insert (&dict, "method", "s", method);
+  g_variant_dict_insert_value (&dict, "params", params);
+  message = g_variant_dict_end (&dict);
 
   ret = jsonrpc_output_stream_write_message (priv->output_stream, message, cancellable, error);
 
-  json_node_unref (params);
-
   return ret;
 }
 
@@ -1001,7 +981,7 @@ jsonrpc_client_send_notification (JsonrpcClient  *self,
  * jsonrpc_client_send_notification_async:
  * @self: A #JsonrpcClient
  * @method: the name of the method to call
- * @params: (transfer full) (nullable): A #JsonNode of parameters or %NULL
+ * @params: (transfer none) (nullable): A #GVariant of parameters or %NULL
  * @cancellable: (nullable): A #GCancellable or %NULL
  *
  * Asynchronously calls @method with @params on the remote peer.
@@ -1011,20 +991,21 @@ jsonrpc_client_send_notification (JsonrpcClient  *self,
  * the bytes have been delivered to the underlying stream. This does
  * not indicate that the peer has received them.
  *
- * This function takes ownership of @params.
+ * If @params is floating then the reference is consumed.
  */
 void
 jsonrpc_client_send_notification_async (JsonrpcClient       *self,
                                         const gchar         *method,
-                                        JsonNode            *params,
+                                        GVariant            *params,
                                         GCancellable        *cancellable,
                                         GAsyncReadyCallback  callback,
                                         gpointer             user_data)
 {
   JsonrpcClientPrivate *priv = jsonrpc_client_get_instance_private (self);
-  g_autoptr(JsonNode) message = NULL;
+  g_autoptr(GVariant) message = NULL;
   g_autoptr(GTask) task = NULL;
   g_autoptr(GError) error = NULL;
+  GVariantDict dict;
 
   g_return_if_fail (JSONRPC_IS_CLIENT (self));
   g_return_if_fail (method != NULL);
@@ -1040,21 +1021,19 @@ jsonrpc_client_send_notification_async (JsonrpcClient       *self,
     }
 
   if (params == NULL)
-    params = json_node_new (JSON_NODE_NULL);
+    params = g_variant_new_maybe (G_VARIANT_TYPE_VARIANT, NULL);
 
-  message = JCON_NEW (
-    "jsonrpc", "2.0",
-    "method", JCON_STRING (method),
-    "params", JCON_NODE (params)
-  );
+  g_variant_dict_init (&dict, NULL);
+  g_variant_dict_insert (&dict, "jsonrpc", "s", "2.0");
+  g_variant_dict_insert (&dict, "method", "s", method);
+  g_variant_dict_insert_value (&dict, "params", params);
+  message = g_variant_dict_end (&dict);
 
   jsonrpc_output_stream_write_message_async (priv->output_stream,
                                              message,
                                              cancellable,
                                              jsonrpc_client_send_notification_write_cb,
                                              g_steal_pointer (&task));
-
-  json_node_unref (params);
 }
 
 /**
@@ -1194,20 +1173,21 @@ jsonrpc_client_close_finish (JsonrpcClient  *self,
 
 /**
  * jsonrpc_client_reply:
- * @id: (transfer full) (not nullable): the id of the message to reply
- * result: (transfer full) (nullable): the return value or %NULL
+ * @id: (transfer none) (not nullable): the id of the message to reply
+ * result: (transfer none) (nullable): the return value or %NULL
  *
  * Synchronous variant of jsonrpc_client_reply_async().
  */
 gboolean
 jsonrpc_client_reply (JsonrpcClient  *self,
-                      JsonNode       *id,
-                      JsonNode       *result,
+                      GVariant       *id,
+                      GVariant       *result,
                       GCancellable   *cancellable,
                       GError        **error)
 {
   JsonrpcClientPrivate *priv = jsonrpc_client_get_instance_private (self);
-  g_autoptr(JsonNode) message = NULL;
+  g_autoptr(GVariant) message = NULL;
+  GVariantDict dict;
   gboolean ret;
 
   g_return_val_if_fail (JSONRPC_IS_CLIENT (self), FALSE);
@@ -1218,19 +1198,16 @@ jsonrpc_client_reply (JsonrpcClient  *self,
     return FALSE;
 
   if (result == NULL)
-    result = json_node_new (JSON_NODE_NULL);
+    result = g_variant_new_maybe (G_VARIANT_TYPE_VARIANT, NULL);
 
-  message = JCON_NEW (
-    "jsonrpc", "2.0",
-    "id", JCON_NODE (id),
-    "result", JCON_NODE (result)
-  );
+  g_variant_dict_init (&dict, NULL);
+  g_variant_dict_insert (&dict, "jsonrpc", "s", "2.0");
+  g_variant_dict_insert_value (&dict, "id", id);
+  g_variant_dict_insert_value (&dict, "result", result);
+  message = g_variant_dict_end (&dict);
 
   ret = jsonrpc_output_stream_write_message (priv->output_stream, message, cancellable, error);
 
-  json_node_unref (id);
-  json_node_unref (result);
-
   return ret;
 }
 
@@ -1276,16 +1253,17 @@ jsonrpc_client_reply_cb (GObject      *object,
  */
 void
 jsonrpc_client_reply_async (JsonrpcClient       *self,
-                            JsonNode            *id,
-                            JsonNode            *result,
+                            GVariant            *id,
+                            GVariant            *result,
                             GCancellable        *cancellable,
                             GAsyncReadyCallback  callback,
                             gpointer             user_data)
 {
   JsonrpcClientPrivate *priv = jsonrpc_client_get_instance_private (self);
   g_autoptr(GTask) task = NULL;
-  g_autoptr(JsonNode) message = NULL;
+  g_autoptr(GVariant) message = NULL;
   g_autoptr(GError) error = NULL;
+  GVariantDict dict;
 
   g_return_if_fail (JSONRPC_IS_CLIENT (self));
   g_return_if_fail (id != NULL);
@@ -1301,22 +1279,19 @@ jsonrpc_client_reply_async (JsonrpcClient       *self,
     }
 
   if (result == NULL)
-    result = json_node_new (JSON_NODE_NULL);
+    result = g_variant_new_maybe (G_VARIANT_TYPE_VARIANT, NULL);
 
-  message = JCON_NEW (
-    "jsonrpc", "2.0",
-    "id", JCON_NODE (id),
-    "result", JCON_NODE (result)
-  );
+  g_variant_dict_init (&dict, NULL);
+  g_variant_dict_insert (&dict, "jsonrpc", "s", "2.0");
+  g_variant_dict_insert_value (&dict, "id", id);
+  g_variant_dict_insert_value (&dict, "result", result);
+  message = g_variant_dict_end (&dict);
 
   jsonrpc_output_stream_write_message_async (priv->output_stream,
                                              message,
                                              cancellable,
                                              jsonrpc_client_reply_cb,
                                              g_steal_pointer (&task));
-
-  json_node_unref (id);
-  json_node_unref (result);
 }
 
 gboolean
@@ -1359,3 +1334,32 @@ jsonrpc_client_start_listening (JsonrpcClient *self)
                                                g_object_ref (self));
     }
 }
+
+gboolean
+jsonrpc_client_get_use_gvariant (JsonrpcClient *self)
+{
+  JsonrpcClientPrivate *priv = jsonrpc_client_get_instance_private (self);
+
+  g_return_val_if_fail (JSONRPC_IS_CLIENT (self), FALSE);
+
+  return priv->use_gvariant;
+}
+
+void
+jsonrpc_client_set_use_gvariant (JsonrpcClient *self,
+                                 gboolean       use_gvariant)
+{
+  JsonrpcClientPrivate *priv = jsonrpc_client_get_instance_private (self);
+
+  g_return_if_fail (JSONRPC_IS_CLIENT (self));
+
+  use_gvariant = !!use_gvariant;
+
+  if (priv->use_gvariant != use_gvariant)
+    {
+      priv->use_gvariant = use_gvariant;
+      if (priv->output_stream != NULL)
+        jsonrpc_output_stream_set_use_gvariant (priv->output_stream, use_gvariant);
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USE_GVARIANT]);
+    }
+}
diff --git a/contrib/jsonrpc-glib/jsonrpc-client.h b/contrib/jsonrpc-glib/jsonrpc-client.h
index 70703f3..ee4f36f 100644
--- a/contrib/jsonrpc-glib/jsonrpc-client.h
+++ b/contrib/jsonrpc-glib/jsonrpc-client.h
@@ -20,7 +20,6 @@
 #define JSONRPC_CLIENT_H
 
 #include <gio/gio.h>
-#include <json-glib/json-glib.h>
 
 G_BEGIN_DECLS
 
@@ -35,11 +34,11 @@ struct _JsonrpcClientClass
 
   void     (*notification) (JsonrpcClient *self,
                             const gchar   *method_name,
-                            JsonNode      *params);
+                            GVariant      *params);
   gboolean (*handle_call)  (JsonrpcClient *self,
                             const gchar   *method,
-                            JsonNode      *id,
-                            JsonNode      *params);
+                            GVariant      *id,
+                            GVariant      *params);
 
   gpointer _reserved1;
   gpointer _reserved2;
@@ -53,6 +52,9 @@ struct _JsonrpcClientClass
 
 GQuark         jsonrpc_client_error_quark              (void);
 JsonrpcClient *jsonrpc_client_new                      (GIOStream            *io_stream);
+gboolean       jsonrpc_client_get_use_gvariant         (JsonrpcClient        *self);
+void           jsonrpc_client_set_use_gvariant         (JsonrpcClient        *self,
+                                                        gboolean              use_gvariant);
 gboolean       jsonrpc_client_close                    (JsonrpcClient        *self,
                                                         GCancellable         *cancellable,
                                                         GError              **error);
@@ -65,28 +67,28 @@ gboolean       jsonrpc_client_close_finish             (JsonrpcClient        *se
                                                         GError              **error);
 gboolean       jsonrpc_client_call                     (JsonrpcClient        *self,
                                                         const gchar          *method,
-                                                        JsonNode             *params,
+                                                        GVariant             *params,
                                                         GCancellable         *cancellable,
-                                                        JsonNode            **return_value,
+                                                        GVariant            **return_value,
                                                         GError              **error);
 void           jsonrpc_client_call_async               (JsonrpcClient        *self,
                                                         const gchar          *method,
-                                                        JsonNode             *params,
+                                                        GVariant             *params,
                                                         GCancellable         *cancellable,
                                                         GAsyncReadyCallback   callback,
                                                         gpointer              user_data);
 gboolean       jsonrpc_client_call_finish              (JsonrpcClient        *self,
                                                         GAsyncResult         *result,
-                                                        JsonNode            **return_value,
+                                                        GVariant            **return_value,
                                                         GError              **error);
 gboolean       jsonrpc_client_send_notification        (JsonrpcClient        *self,
                                                         const gchar          *method,
-                                                        JsonNode             *params,
+                                                        GVariant             *params,
                                                         GCancellable         *cancellable,
                                                         GError              **error);
 void           jsonrpc_client_send_notification_async  (JsonrpcClient        *self,
                                                         const gchar          *method,
-                                                        JsonNode             *params,
+                                                        GVariant             *params,
                                                         GCancellable         *cancellable,
                                                         GAsyncReadyCallback   callback,
                                                         gpointer              user_data);
@@ -94,13 +96,13 @@ gboolean       jsonrpc_client_send_notification_finish (JsonrpcClient        *se
                                                         GAsyncResult         *result,
                                                         GError              **error);
 gboolean       jsonrpc_client_reply                    (JsonrpcClient        *self,
-                                                        JsonNode             *id,
-                                                        JsonNode             *result,
+                                                        GVariant             *id,
+                                                        GVariant             *result,
                                                         GCancellable         *cancellable,
                                                         GError              **error);
 void           jsonrpc_client_reply_async              (JsonrpcClient        *self,
-                                                        JsonNode             *id,
-                                                        JsonNode             *result,
+                                                        GVariant             *id,
+                                                        GVariant             *result,
                                                         GCancellable         *cancellable,
                                                         GAsyncReadyCallback   callback,
                                                         gpointer              user_data);
diff --git a/contrib/jsonrpc-glib/jsonrpc-glib.h b/contrib/jsonrpc-glib/jsonrpc-glib.h
index d837f16..4aed245 100644
--- a/contrib/jsonrpc-glib/jsonrpc-glib.h
+++ b/contrib/jsonrpc-glib/jsonrpc-glib.h
@@ -27,10 +27,10 @@ G_BEGIN_DECLS
 #define JSONRPC_GLIB_INSIDE
 # include "jsonrpc-client.h"
 # include "jsonrpc-input-stream.h"
+# include "jsonrpc-message.h"
 # include "jsonrpc-output-stream.h"
 # include "jsonrpc-server.h"
 # include "jsonrpc-version.h"
-# include "jcon.h"
 #undef JSONRPC_GLIB_INSIDE
 
 G_END_DECLS
diff --git a/contrib/jsonrpc-glib/jsonrpc-input-stream-private.h 
b/contrib/jsonrpc-glib/jsonrpc-input-stream-private.h
new file mode 100644
index 0000000..586ec38
--- /dev/null
+++ b/contrib/jsonrpc-glib/jsonrpc-input-stream-private.h
@@ -0,0 +1,30 @@
+/* jsonrpc-input-stream-private.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef JSONRPC_INPUT_STREAM_PRIVATE_H
+#define JSONRPC_INPUT_STREAM_PRIVATE_H
+
+#include "jsonrpc-input-stream.h"
+
+G_BEGIN_DECLS
+
+gboolean _jsonrpc_input_stream_get_has_seen_gvariant (JsonrpcInputStream *self);
+
+G_END_DECLS
+
+#endif /* JSONRPC_INPUT_STREAM_PRIVATE_H */
diff --git a/contrib/jsonrpc-glib/jsonrpc-input-stream.c b/contrib/jsonrpc-glib/jsonrpc-input-stream.c
index 248d27a..78069d2 100644
--- a/contrib/jsonrpc-glib/jsonrpc-input-stream.c
+++ b/contrib/jsonrpc-glib/jsonrpc-input-stream.c
@@ -19,20 +19,25 @@
 #define G_LOG_DOMAIN "jsonrpc-input-stream"
 
 #include <errno.h>
+#include <json-glib/json-glib.h>
 #include <string.h>
 
 #include "jsonrpc-input-stream.h"
+#include "jsonrpc-input-stream-private.h"
 
 typedef struct
 {
-  gssize content_length;
-  gchar *buffer;
-  gint priority;
+  gssize        content_length;
+  gchar        *buffer;
+  GVariantType *gvariant_type;
+  gint16        priority;
+  gint          use_gvariant : 1;
 } ReadState;
 
 typedef struct
 {
   gssize max_size_bytes;
+  guint has_seen_gvariant : 1;
 } JsonrpcInputStreamPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (JsonrpcInputStream, jsonrpc_input_stream, G_TYPE_DATA_INPUT_STREAM)
@@ -45,6 +50,7 @@ read_state_free (gpointer data)
   ReadState *state = data;
 
   g_free (state->buffer);
+  g_free (state->gvariant_type);
   g_slice_free (ReadState, state);
 }
 
@@ -81,10 +87,9 @@ jsonrpc_input_stream_read_body_cb (GObject      *object,
 {
   JsonrpcInputStream *self = (JsonrpcInputStream *)object;
   g_autoptr(GTask) task = user_data;
-  g_autoptr(JsonParser) parser = NULL;
   g_autoptr(GError) error = NULL;
+  g_autoptr(GVariant) message = NULL;
   ReadState *state;
-  JsonNode *root;
   gsize n_read;
 
   g_assert (JSONRPC_IS_INPUT_STREAM (self));
@@ -110,31 +115,34 @@ jsonrpc_input_stream_read_body_cb (GObject      *object,
 
   state->buffer [state->content_length] = '\0';
 
-  if G_UNLIKELY (jsonrpc_input_stream_debug)
+  if G_UNLIKELY (jsonrpc_input_stream_debug && state->use_gvariant == FALSE)
     g_message ("<<< %s", state->buffer);
 
-  parser = json_parser_new_immutable ();
-
-  if (!json_parser_load_from_data (parser, state->buffer, state->content_length, &error))
+  if (state->use_gvariant)
     {
-      g_task_return_error (task, g_steal_pointer (&error));
-      return;
+      message = g_variant_new_from_data (state->gvariant_type ?  state->gvariant_type
+                                                              : G_VARIANT_TYPE_VARDICT,
+                                         state->buffer,
+                                         state->content_length,
+                                         FALSE,
+                                         g_free,
+                                         state->buffer);
+      state->buffer = NULL;
+    }
+  else
+    {
+      message = json_gvariant_deserialize_data (state->buffer, state->content_length, NULL, &error);
     }
 
-  if (NULL == (root = json_parser_get_root (parser)))
+  g_assert (message != NULL || error != NULL);
+
+  if (error != NULL)
     {
-      /*
-       * If we get back a NULL root node, that means that we got
-       * a short read (such as a closed stream).
-       */
-      g_task_return_new_error (task,
-                               G_IO_ERROR,
-                               G_IO_ERROR_CLOSED,
-                               "The peer did not send a reply");
+      g_task_return_error (task, g_steal_pointer (&error));
       return;
     }
 
-  g_task_return_pointer (task, json_node_copy (root), (GDestroyNotify)json_node_unref);
+  g_task_return_pointer (task, g_steal_pointer (&message), (GDestroyNotify)g_variant_unref);
 }
 
 static void
@@ -154,23 +162,19 @@ jsonrpc_input_stream_read_headers_cb (GObject      *object,
   g_assert (JSONRPC_IS_INPUT_STREAM (self));
   g_assert (G_IS_TASK (task));
 
+  state = g_task_get_task_data (task);
+  cancellable = g_task_get_cancellable (task);
+
   line = g_data_input_stream_read_line_finish_utf8 (G_DATA_INPUT_STREAM (self), result, &length, &error);
 
   if (line == NULL)
     {
-      if (error != NULL)
-        g_task_return_error (task, g_steal_pointer (&error));
-      else
-        g_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_CLOSED,
-                                 "The peer has closed the stream");
+      if (error == NULL)
+        goto read_next_line;
+      g_task_return_error (task, g_steal_pointer (&error));
       return;
     }
 
-  state = g_task_get_task_data (task);
-  cancellable = g_task_get_cancellable (task);
-
   if (strncasecmp ("Content-Length: ", line, 16) == 0)
     {
       const gchar *lenptr = line + 16;
@@ -180,6 +184,7 @@ jsonrpc_input_stream_read_headers_cb (GObject      *object,
 
       if (((content_length == G_MININT64 || content_length == G_MAXINT64) && errno == ERANGE) ||
           (content_length < 0) ||
+          (content_length > G_MAXSSIZE) ||
           (content_length > priv->max_size_bytes))
         {
           g_task_return_new_error (task,
@@ -192,6 +197,29 @@ jsonrpc_input_stream_read_headers_cb (GObject      *object,
       state->content_length = content_length;
     }
 
+  if (strncasecmp ("Content-Type: ", line, 14) == 0)
+    {
+      if (NULL != strstr (line, "application/gvariant"))
+        state->use_gvariant = TRUE;
+    }
+
+  if (strncasecmp ("X-GVariant-Type: ", line, 17) == 0)
+    {
+      const gchar *type_string = line + 17;
+
+      if (!g_variant_type_string_is_valid (type_string))
+        {
+          g_task_return_new_error (task,
+                                   G_IO_ERROR,
+                                   G_IO_ERROR_INVALID_DATA,
+                                   "Invalid X-GVariant-Type received from peer");
+          return;
+        }
+
+      g_clear_pointer (&state->gvariant_type, g_free);
+      state->gvariant_type = (GVariantType *)g_strdup (type_string);
+    }
+
   /*
    * If we are at the end of the headers, we can make progress towards
    * parsing the JSON content. Otherwise we need to continue parsing
@@ -220,6 +248,7 @@ jsonrpc_input_stream_read_headers_cb (GObject      *object,
       return;
     }
 
+read_next_line:
   g_data_input_stream_read_line_async (G_DATA_INPUT_STREAM (self),
                                        state->priority,
                                        cancellable,
@@ -257,20 +286,26 @@ jsonrpc_input_stream_read_message_async (JsonrpcInputStream  *self,
 gboolean
 jsonrpc_input_stream_read_message_finish (JsonrpcInputStream  *self,
                                           GAsyncResult        *result,
-                                          JsonNode           **node,
+                                          GVariant           **message,
                                           GError             **error)
 {
-  g_autoptr(JsonNode) local_node = NULL;
+  JsonrpcInputStreamPrivate *priv = jsonrpc_input_stream_get_instance_private (self);
+  g_autoptr(GVariant) local_message = NULL;
+  ReadState *state;
   gboolean ret;
 
   g_return_val_if_fail (JSONRPC_IS_INPUT_STREAM (self), FALSE);
   g_return_val_if_fail (G_IS_TASK (result), FALSE);
 
-  local_node = g_task_propagate_pointer (G_TASK (result), error);
-  ret = local_node != NULL;
+  /* track if we've seen an application/gvariant */
+  state = g_task_get_task_data (G_TASK (result));
+  priv->has_seen_gvariant |= state->use_gvariant;
+
+  local_message = g_task_propagate_pointer (G_TASK (result), error);
+  ret = local_message != NULL;
 
-  if (node != NULL)
-    *node = g_steal_pointer (&local_node);
+  if (message != NULL)
+    *message = g_steal_pointer (&local_message);
 
   return ret;
 }
@@ -282,26 +317,26 @@ jsonrpc_input_stream_read_message_sync_cb (GObject      *object,
 {
   JsonrpcInputStream *self = (JsonrpcInputStream *)object;
   g_autoptr(GError) error = NULL;
-  g_autoptr(JsonNode) node = NULL;
+  g_autoptr(GVariant) message = NULL;
   GTask *task = user_data;
 
   g_assert (JSONRPC_IS_INPUT_STREAM (self));
   g_assert (G_IS_TASK (task));
 
-  if (!jsonrpc_input_stream_read_message_finish (self, result, &node, &error))
+  if (!jsonrpc_input_stream_read_message_finish (self, result, &message, &error))
     g_task_return_error (task, g_steal_pointer (&error));
   else
-    g_task_return_pointer (task, g_steal_pointer (&node), (GDestroyNotify)json_node_unref);
+    g_task_return_pointer (task, g_steal_pointer (&message), (GDestroyNotify)g_variant_unref);
 }
 
 gboolean
 jsonrpc_input_stream_read_message (JsonrpcInputStream  *self,
                                    GCancellable        *cancellable,
-                                   JsonNode           **node,
+                                   GVariant           **message,
                                    GError             **error)
 {
   g_autoptr(GMainContext) main_context = NULL;
-  g_autoptr(JsonNode) local_node = NULL;
+  g_autoptr(GVariant) local_message = NULL;
   g_autoptr(GTask) task = NULL;
   gboolean ret;
 
@@ -321,12 +356,21 @@ jsonrpc_input_stream_read_message (JsonrpcInputStream  *self,
   while (!g_task_get_completed (task))
     g_main_context_iteration (main_context, TRUE);
 
-  local_node = g_task_propagate_pointer (task, error);
-  ret = local_node != NULL;
+  local_message = g_task_propagate_pointer (task, error);
+  ret = local_message != NULL;
 
-  if (node != NULL)
-    *node = g_steal_pointer (&local_node);
+  if (message != NULL)
+    *message = g_steal_pointer (&local_message);
 
   return ret;
 }
 
+gboolean
+_jsonrpc_input_stream_get_has_seen_gvariant (JsonrpcInputStream *self)
+{
+  JsonrpcInputStreamPrivate *priv = jsonrpc_input_stream_get_instance_private (self);
+
+  g_return_val_if_fail (JSONRPC_IS_INPUT_STREAM (self), FALSE);
+
+  return priv->has_seen_gvariant;
+}
diff --git a/contrib/jsonrpc-glib/jsonrpc-input-stream.h b/contrib/jsonrpc-glib/jsonrpc-input-stream.h
index 954118c..043b3e2 100644
--- a/contrib/jsonrpc-glib/jsonrpc-input-stream.h
+++ b/contrib/jsonrpc-glib/jsonrpc-input-stream.h
@@ -20,7 +20,6 @@
 #define JSONRPC_INPUT_STREAM_H
 
 #include <gio/gio.h>
-#include <json-glib/json-glib.h>
 
 G_BEGIN_DECLS
 
@@ -45,7 +44,7 @@ struct _JsonrpcInputStreamClass
 JsonrpcInputStream *jsonrpc_input_stream_new                 (GInputStream         *base_stream);
 gboolean            jsonrpc_input_stream_read_message        (JsonrpcInputStream   *self,
                                                               GCancellable         *cancellable,
-                                                              JsonNode            **node,
+                                                              GVariant            **message,
                                                               GError              **error);
 void                jsonrpc_input_stream_read_message_async  (JsonrpcInputStream   *self,
                                                               GCancellable         *cancellable,
@@ -53,7 +52,7 @@ void                jsonrpc_input_stream_read_message_async  (JsonrpcInputStream
                                                               gpointer              user_data);
 gboolean            jsonrpc_input_stream_read_message_finish (JsonrpcInputStream   *self,
                                                               GAsyncResult         *result,
-                                                              JsonNode            **node,
+                                                              GVariant            **message,
                                                               GError              **error);
 
 G_END_DECLS
diff --git a/contrib/jsonrpc-glib/jsonrpc-message.c b/contrib/jsonrpc-glib/jsonrpc-message.c
new file mode 100644
index 0000000..1d53084
--- /dev/null
+++ b/contrib/jsonrpc-glib/jsonrpc-message.c
@@ -0,0 +1,475 @@
+/* jsonrpc-message.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "jsonrpc-message"
+
+#include <string.h>
+
+#include "jsonrpc-message.h"
+
+#if 1
+# define ENTRY     do { g_print (" ENTRY: %s(): %d\n", G_STRFUNC, __LINE__); } while (0)
+# define RETURN(r) do { g_print ("RETURN: %s(): %d\n", G_STRFUNC, __LINE__); return r; } while (0)
+# define EXIT      do { g_print ("  EXIT: %s(): %d\n", G_STRFUNC, __LINE__); return; } while (0)
+#else
+# define ENTRY     do { } while (0)
+# define RETURN(r) do { return r; } while (0)
+# define EXIT      do { return; } while (0)
+#endif
+
+#define COMPARE_MAGIC(_any,_magic) \
+  (strncmp ((_any)->magic.bytes, \
+            _JSONRPC_MESSAGE_##_magic##_MAGIC, \
+            sizeof ((_any)->magic.bytes)) == 0)
+
+#define IS_PUT_STRING(_any)  COMPARE_MAGIC(_any, PUT_STRING)
+#define IS_PUT_INT32(_any)   COMPARE_MAGIC(_any, PUT_INT32)
+#define IS_PUT_BOOLEAN(_any) COMPARE_MAGIC(_any, PUT_BOOLEAN)
+#define IS_PUT_DOUBLE(_any)  COMPARE_MAGIC(_any, PUT_DOUBLE)
+
+#define IS_GET_STRING(_any)  COMPARE_MAGIC(_any, GET_STRING)
+#define IS_GET_INT32(_any)   COMPARE_MAGIC(_any, GET_INT32)
+#define IS_GET_BOOLEAN(_any) COMPARE_MAGIC(_any, GET_BOOLEAN)
+#define IS_GET_DOUBLE(_any)  COMPARE_MAGIC(_any, GET_DOUBLE)
+#define IS_GET_ITER(_any)    COMPARE_MAGIC(_any, GET_ITER)
+#define IS_GET_DICT(_any)    COMPARE_MAGIC(_any, GET_DICT)
+#define IS_GET_VARIANT(_any) COMPARE_MAGIC(_any, GET_VARIANT)
+
+static void     jsonrpc_message_build_object   (GVariantBuilder *builder,
+                                                gpointer         param,
+                                                va_list          args);
+static void     jsonrpc_message_build_array    (GVariantBuilder *builder,
+                                                gpointer         param,
+                                                va_list          args);
+static gboolean jsonrpc_message_parse_object   (GVariantDict    *dict,
+                                                gpointer         param,
+                                                va_list          args);
+static gboolean jsonrpc_message_parse_array_va (GVariantIter    *iter,
+                                                gpointer         param,
+                                                va_list          args);
+
+static void
+jsonrpc_message_build_object (GVariantBuilder *builder,
+                              gpointer         param,
+                              va_list          args)
+{
+  JsonrpcMessageAny *keyptr = param;
+  JsonrpcMessageAny *valptr;
+  const char *key;
+
+  ENTRY;
+
+  /*
+   * First we need to get the key for this item. If we have reached
+   * the '}', then we either have an empty {} or got here via
+   * recursion to read the next key/val pair.
+   */
+
+  if (!keyptr || keyptr->magic.bytes[0] == '}')
+    EXIT;
+
+  g_variant_builder_open (builder, G_VARIANT_TYPE ("{sv}"));
+
+  /*
+   * Either this is a string wrapped in JSONRPC_MESSAGE_PUT_STRING() or
+   * we assume it is a raw key name like "foo".
+   */
+  if (IS_PUT_STRING (keyptr))
+    key = ((JsonrpcMessagePutString *)keyptr)->val;
+  else
+    key = (const char *)keyptr;
+
+  g_variant_builder_add (builder, "s", key);
+
+  /*
+   * Now try to read the value for the key/val pair.
+   */
+  valptr = param = va_arg (args, gpointer);
+
+  g_variant_builder_open (builder, G_VARIANT_TYPE ("v"));
+
+  switch (valptr->magic.bytes[0])
+    {
+    case '{':
+      g_variant_builder_open (builder, G_VARIANT_TYPE ("a{sv}"));
+      param = va_arg (args, gpointer);
+      jsonrpc_message_build_object (builder, param, args);
+      g_variant_builder_close (builder);
+      break;
+
+    case '[':
+      g_variant_builder_open (builder, G_VARIANT_TYPE ("av"));
+      param = va_arg (args, gpointer);
+      jsonrpc_message_build_array (builder, param, args);
+      g_variant_builder_close (builder);
+      break;
+
+    case ']':
+    case '}':
+      g_return_if_reached ();
+      break;
+
+    default:
+      if (IS_PUT_STRING (valptr))
+        g_variant_builder_add (builder, "s", ((JsonrpcMessagePutString *)valptr)->val);
+      else if (IS_PUT_INT32 (valptr))
+        g_variant_builder_add (builder, "i", ((JsonrpcMessagePutInt32 *)valptr)->val);
+      else if (IS_PUT_BOOLEAN (valptr))
+        g_variant_builder_add (builder, "b", ((JsonrpcMessagePutBoolean *)valptr)->val);
+      else if (IS_PUT_DOUBLE (valptr))
+        g_variant_builder_add (builder, "d", ((JsonrpcMessagePutDouble *)valptr)->val);
+      else
+        g_variant_builder_add (builder, "s", (const char *)valptr);
+      break;
+    }
+
+  g_variant_builder_close (builder);
+  g_variant_builder_close (builder);
+
+  /*
+   * Try to build the next field in the object if there is one.
+   */
+  param = va_arg (args, gpointer);
+  jsonrpc_message_build_object (builder, param, args);
+
+  EXIT;
+}
+
+static void
+jsonrpc_message_build_array (GVariantBuilder *builder,
+                             gpointer         param,
+                             va_list          args)
+{
+  JsonrpcMessageAny *valptr = param;
+
+  ENTRY;
+
+  /* If we have the end of the array, we're done */
+  if (valptr->magic.bytes[0] == ']')
+    EXIT;
+
+  g_variant_builder_open (builder, G_VARIANT_TYPE ("v"));
+
+  switch (valptr->magic.bytes[0])
+    {
+    case '{':
+      g_variant_builder_open (builder, G_VARIANT_TYPE ("a{sv}"));
+      param = va_arg (args, gpointer);
+      jsonrpc_message_build_object (builder, param, args);
+      g_variant_builder_close (builder);
+      break;
+
+    case '[':
+      g_variant_builder_open (builder, G_VARIANT_TYPE ("av"));
+      param = va_arg (args, gpointer);
+      jsonrpc_message_build_array (builder, param, args);
+      g_variant_builder_close (builder);
+      break;
+
+    case ']':
+    case '}':
+      g_return_if_reached ();
+      break;
+
+    default:
+      if (IS_PUT_STRING (valptr))
+        g_variant_builder_add (builder, "s", ((JsonrpcMessagePutString *)valptr)->val);
+      else if (IS_PUT_INT32 (valptr))
+        g_variant_builder_add (builder, "i", ((JsonrpcMessagePutInt32 *)valptr)->val);
+      else if (IS_PUT_BOOLEAN (valptr))
+        g_variant_builder_add (builder, "b", ((JsonrpcMessagePutBoolean *)valptr)->val);
+      else if (IS_PUT_DOUBLE (valptr))
+        g_variant_builder_add (builder, "d", ((JsonrpcMessagePutDouble *)valptr)->val);
+      else
+        g_variant_builder_add (builder, "s", (const char *)valptr);
+      break;
+    }
+
+  g_variant_builder_close (builder);
+
+  param = va_arg (args, gpointer);
+  jsonrpc_message_build_array (builder, param, args);
+
+  EXIT;
+}
+
+static GVariant *
+jsonrpc_message_new_valist (gpointer first_param,
+                            va_list  args)
+{
+  GVariantBuilder builder;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+  if (first_param != NULL)
+    jsonrpc_message_build_object (&builder, first_param, args);
+
+  return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+GVariant *
+jsonrpc_message_new (gpointer first_param,
+                     ...)
+{
+  GVariant *ret;
+  va_list args;
+
+  g_return_val_if_fail (first_param != NULL, NULL);
+
+  va_start (args, first_param);
+  ret = jsonrpc_message_new_valist (first_param, args);
+  va_end (args);
+
+  return ret;
+}
+
+static gboolean
+jsonrpc_message_parse_object (GVariantDict *dict,
+                              gpointer      param,
+                              va_list       args)
+{
+  JsonrpcMessageAny *valptr;
+  const char *key = param;
+  gboolean ret = FALSE;
+
+  ENTRY;
+
+  g_assert (dict != NULL);
+
+  if (key == NULL || key[0] == '}')
+    RETURN (TRUE);
+
+  valptr = va_arg (args, gpointer);
+
+  if (valptr == NULL)
+    g_error ("got unexpected NULL for key %s", key);
+
+  if (valptr->magic.bytes[0] == '{' || IS_GET_DICT (valptr))
+    {
+      g_autoptr(GVariant) value = NULL;
+
+      if (NULL != (value = g_variant_dict_lookup_value (dict, key, G_VARIANT_TYPE ("a{sv}"))))
+        {
+          g_autoptr(GVariantDict) subdict = NULL;
+
+          subdict = g_variant_dict_new (value);
+
+          g_assert (subdict != NULL);
+
+          if (IS_GET_DICT (valptr))
+            ret = !!(*((JsonrpcMessageGetDict *)valptr)->dictptr = g_steal_pointer (&subdict));
+          else
+            {
+              param = va_arg (args, gpointer);
+              ret = jsonrpc_message_parse_object (subdict, param, args);
+            }
+        }
+    }
+  else if (valptr->magic.bytes[0] == '[' || IS_GET_ITER (valptr))
+    {
+      g_autoptr(GVariantIter) subiter = NULL;
+
+      if (g_variant_dict_lookup (dict, key, "av", &subiter))
+        {
+          if (IS_GET_ITER (valptr))
+            ret = !!(*((JsonrpcMessageGetIter *)valptr)->iterptr = g_steal_pointer (&subiter));
+          else
+            {
+              param = va_arg (args, gpointer);
+              ret = jsonrpc_message_parse_array_va (subiter, param, args);
+            }
+        }
+    }
+  else if (IS_GET_VARIANT (valptr))
+    ret = !!(*((JsonrpcMessageGetVariant *)valptr)->variantptr = g_variant_dict_lookup_value (dict, key, 
NULL));
+  else if (IS_GET_STRING (valptr))
+    ret = g_variant_dict_lookup (dict, key, "&s", ((JsonrpcMessageGetString *)valptr)->valptr);
+  else if (IS_GET_INT32 (valptr))
+    ret = g_variant_dict_lookup (dict, key, "i", ((JsonrpcMessageGetInt32 *)valptr)->valptr);
+  else if (IS_GET_BOOLEAN (valptr))
+    ret = g_variant_dict_lookup (dict, key, "b", ((JsonrpcMessageGetBoolean *)valptr)->valptr);
+  else if (IS_GET_DOUBLE (valptr))
+    ret = g_variant_dict_lookup (dict, key, "d", ((JsonrpcMessageGetDouble *)valptr)->valptr);
+  else
+    {
+      /* Assume the string is a raw string, so compare for equality */
+      const gchar *valstr = NULL;
+      ret = g_variant_dict_lookup (dict, key, "&s", &valstr) && g_strcmp0 (valstr, (const char *)valptr) == 
0;
+    }
+
+  if (!ret || !param)
+    RETURN (ret);
+
+  /* If we succeeded, try to read the next field */
+  param = va_arg (args, gpointer);
+  ret = jsonrpc_message_parse_object (dict, param, args);
+
+  RETURN (ret);
+}
+
+static gboolean
+jsonrpc_message_parse_array_va (GVariantIter *iter,
+                                gpointer      param,
+                                va_list       args)
+{
+  JsonrpcMessageAny *valptr = param;
+  g_autoptr(GVariant) value = NULL;
+  gboolean ret = FALSE;
+
+  ENTRY;
+
+  g_assert (iter != NULL);
+
+  if (valptr == NULL || valptr->magic.bytes[0] == ']')
+    RETURN (TRUE);
+
+  if (!g_variant_iter_next (iter, "v", &value))
+    RETURN (FALSE);
+
+  if (valptr->magic.bytes[0] == '{' || IS_GET_DICT (valptr))
+    {
+      if (g_variant_is_of_type (value, G_VARIANT_TYPE ("a{sv}")))
+        {
+          g_autoptr(GVariantDict) subdict = NULL;
+
+          subdict = g_variant_dict_new (value);
+
+          if (IS_GET_ITER (valptr))
+            ret = !!(*((JsonrpcMessageGetDict *)valptr)->dictptr = g_steal_pointer (&subdict));
+          else
+            {
+              param = va_arg (args, gpointer);
+              ret = jsonrpc_message_parse_object (subdict, param, args);
+            }
+        }
+    }
+  else if (valptr->magic.bytes[0] == '[' || IS_GET_ITER (valptr))
+    {
+      if (g_variant_is_of_type (value, G_VARIANT_TYPE ("av")))
+        {
+          g_autoptr(GVariantIter) subiter = NULL;
+
+          subiter = g_variant_iter_new (value);
+
+          if (IS_GET_ITER (valptr))
+            ret = !!(*((JsonrpcMessageGetIter *)valptr)->iterptr = g_steal_pointer (&subiter));
+          else
+            {
+              param = va_arg (args, gpointer);
+              ret = jsonrpc_message_parse_array_va (subiter, param, args);
+            }
+        }
+    }
+  else if (IS_GET_VARIANT (valptr))
+    {
+      ret = !!(*((JsonrpcMessageGetVariant *)valptr)->variantptr = g_steal_pointer (&value));
+    }
+  else if (IS_GET_STRING (valptr))
+    {
+      if ((ret = g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)))
+        *((JsonrpcMessageGetString *)valptr)->valptr = g_variant_get_string (value, NULL);
+    }
+  else if (IS_GET_INT32 (valptr))
+    {
+      if ((ret = g_variant_is_of_type (value, G_VARIANT_TYPE_INT32)))
+        *((JsonrpcMessageGetInt32 *)valptr)->valptr = g_variant_get_int32 (value);
+    }
+  else if (IS_GET_BOOLEAN (valptr))
+    {
+      if ((ret = g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)))
+        *((JsonrpcMessageGetBoolean *)valptr)->valptr = g_variant_get_boolean (value);
+    }
+  else if (IS_GET_DOUBLE (valptr))
+    {
+      if ((ret = g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE)))
+        *((JsonrpcMessageGetDouble *)valptr)->valptr = g_variant_get_double (value);
+    }
+  else
+    {
+      /* Assume the string is a raw string, so compare for equality */
+      ret = g_variant_is_of_type (value, G_VARIANT_TYPE_STRING) &&
+            (0 == g_strcmp0 (g_variant_get_string (value, NULL), (const gchar *)valptr));
+    }
+
+  if (!ret || !param)
+    RETURN (ret);
+
+  /* If we succeeded, try to read the next element */
+  param = va_arg (args, gpointer);
+  ret = jsonrpc_message_parse_array_va (iter, param, args);
+
+  RETURN (ret);
+}
+
+gboolean
+jsonrpc_message_parse_array (GVariantIter *iter, ...)
+{
+  va_list args;
+  gpointer param;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (iter != NULL, FALSE);
+
+  va_start (args, iter);
+  param = va_arg (args, gpointer);
+  if (param)
+    ret = jsonrpc_message_parse_array_va (iter, param, args);
+  va_end (args);
+
+  return ret;
+}
+
+static gboolean
+jsonrpc_message_parse_valist (GVariant *message,
+                              va_list   args)
+{
+  gpointer param;
+  gboolean ret = FALSE;
+
+  g_assert (message != NULL);
+  g_assert (g_variant_is_of_type (message, G_VARIANT_TYPE ("a{sv}")));
+
+  param = va_arg (args, gpointer);
+
+  if (param != NULL)
+    {
+      GVariantDict dict;
+
+      g_variant_dict_init (&dict, message);
+      ret = jsonrpc_message_parse_object (&dict, param, args);
+      g_variant_dict_clear (&dict);
+    }
+
+  return ret;
+}
+
+gboolean
+jsonrpc_message_parse (GVariant *message, ...)
+{
+  gboolean ret;
+  va_list args;
+
+  g_return_val_if_fail (message != NULL, FALSE);
+  g_return_val_if_fail (g_variant_is_of_type (message, G_VARIANT_TYPE ("a{sv}")), FALSE);
+
+  va_start (args, message);
+  ret = jsonrpc_message_parse_valist (message, args);
+  va_end (args);
+
+  return ret;
+}
diff --git a/contrib/jsonrpc-glib/jsonrpc-message.h b/contrib/jsonrpc-glib/jsonrpc-message.h
new file mode 100644
index 0000000..b965883
--- /dev/null
+++ b/contrib/jsonrpc-glib/jsonrpc-message.h
@@ -0,0 +1,171 @@
+/* jsonrpc-message.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef JSONRPC_MESSAGE_H
+#define JSONRPC_MESSAGE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+  char bytes[8];
+} JsonrpcMessageMagic;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+} JsonrpcMessageAny;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  const char *val;
+} JsonrpcMessagePutString;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  const char **valptr;
+} JsonrpcMessageGetString;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  gint32 val;
+} JsonrpcMessagePutInt32;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  gint32 *valptr;
+} JsonrpcMessageGetInt32;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  gboolean val;
+} JsonrpcMessagePutBoolean;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  gboolean *valptr;
+} JsonrpcMessageGetBoolean;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  double val;
+} JsonrpcMessagePutDouble;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  double *valptr;
+} JsonrpcMessageGetDouble;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  GVariantIter **iterptr;
+} JsonrpcMessageGetIter;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  GVariantDict **dictptr;
+} JsonrpcMessageGetDict;
+
+typedef struct
+{
+  JsonrpcMessageMagic magic;
+  GVariant **variantptr;
+} JsonrpcMessageGetVariant;
+
+#define _JSONRPC_MAGIC(s) ("@!^%" s)
+#define _JSONRPC_MAGIC_C(a,b,c,d) {'@','!','^','%',a,b,c,d}
+
+#define _JSONRPC_MESSAGE_PUT_STRING_MAGIC  _JSONRPC_MAGIC("PUTS")
+#define _JSONRPC_MESSAGE_GET_STRING_MAGIC  _JSONRPC_MAGIC("GETS")
+#define _JSONRPC_MESSAGE_PUT_INT32_MAGIC   _JSONRPC_MAGIC("PUTI")
+#define _JSONRPC_MESSAGE_GET_INT32_MAGIC   _JSONRPC_MAGIC("GETI")
+#define _JSONRPC_MESSAGE_PUT_BOOLEAN_MAGIC _JSONRPC_MAGIC("PUTB")
+#define _JSONRPC_MESSAGE_GET_BOOLEAN_MAGIC _JSONRPC_MAGIC("GETB")
+#define _JSONRPC_MESSAGE_PUT_DOUBLE_MAGIC  _JSONRPC_MAGIC("PUTD")
+#define _JSONRPC_MESSAGE_GET_DOUBLE_MAGIC  _JSONRPC_MAGIC("GETD")
+#define _JSONRPC_MESSAGE_GET_ITER_MAGIC    _JSONRPC_MAGIC("GETT")
+#define _JSONRPC_MESSAGE_GET_DICT_MAGIC    _JSONRPC_MAGIC("GETC")
+#define _JSONRPC_MESSAGE_GET_VARIANT_MAGIC _JSONRPC_MAGIC("GETV")
+
+#define _JSONRPC_MESSAGE_PUT_STRING_MAGIC_C  _JSONRPC_MAGIC_C('P','U','T','S')
+#define _JSONRPC_MESSAGE_GET_STRING_MAGIC_C  _JSONRPC_MAGIC_C('G','E','T','S')
+#define _JSONRPC_MESSAGE_PUT_INT32_MAGIC_C   _JSONRPC_MAGIC_C('P','U','T','I')
+#define _JSONRPC_MESSAGE_GET_INT32_MAGIC_C   _JSONRPC_MAGIC_C('G','E','T','I')
+#define _JSONRPC_MESSAGE_PUT_BOOLEAN_MAGIC_C _JSONRPC_MAGIC_C('P','U','T','B')
+#define _JSONRPC_MESSAGE_GET_BOOLEAN_MAGIC_C _JSONRPC_MAGIC_C('G','E','T','B')
+#define _JSONRPC_MESSAGE_PUT_DOUBLE_MAGIC_C  _JSONRPC_MAGIC_C('P','U','T','D')
+#define _JSONRPC_MESSAGE_GET_DOUBLE_MAGIC_C  _JSONRPC_MAGIC_C('G','E','T','D')
+#define _JSONRPC_MESSAGE_GET_ITER_MAGIC_C    _JSONRPC_MAGIC_C('G','E','T','T')
+#define _JSONRPC_MESSAGE_GET_DICT_MAGIC_C    _JSONRPC_MAGIC_C('G','E','T','C')
+#define _JSONRPC_MESSAGE_GET_VARIANT_MAGIC_C _JSONRPC_MAGIC_C('G','E','T','V')
+
+#define JSONRPC_MESSAGE_NEW(first_, ...) \
+  jsonrpc_message_new(first_, __VA_ARGS__, NULL)
+#define JSONRPC_MESSAGE_PARSE(message, ...) \
+  jsonrpc_message_parse(message,  __VA_ARGS__, NULL)
+#define JSONRPC_MESSAGE_PARSE_ARRAY(iter, ...) \
+  jsonrpc_message_parse_array(iter, __VA_ARGS__, NULL)
+
+#define JSONRPC_MESSAGE_PUT_STRING(_val) \
+  (&((JsonrpcMessagePutString) { .magic = {_JSONRPC_MESSAGE_PUT_STRING_MAGIC_C}, .val = _val }))
+#define JSONRPC_MESSAGE_GET_STRING(_valptr) \
+  (&((JsonrpcMessageGetString) { .magic = {_JSONRPC_MESSAGE_GET_STRING_MAGIC_C}, .valptr = _valptr }))
+
+#define JSONRPC_MESSAGE_PUT_INT32(_val) \
+  (&((JsonrpcMessagePutInt32) { .magic = {_JSONRPC_MESSAGE_PUT_INT32_MAGIC_C}, .val = _val }))
+#define JSONRPC_MESSAGE_GET_INT32(_valptr) \
+  (&((JsonrpcMessageGetInt32) { .magic = {_JSONRPC_MESSAGE_GET_INT32_MAGIC_C}, .valptr = _valptr }))
+
+#define JSONRPC_MESSAGE_PUT_BOOLEAN(_val) \
+  (&((JsonrpcMessagePutBoolean) { .magic = {_JSONRPC_MESSAGE_PUT_BOOLEAN_MAGIC_C}, .val = _val }))
+#define JSONRPC_MESSAGE_GET_BOOLEAN(_valptr) \
+  (&((JsonrpcMessageGetBoolean) { .magic = {_JSONRPC_MESSAGE_GET_BOOLEAN_MAGIC_C}, .valptr = _valptr }))
+
+#define JSONRPC_MESSAGE_PUT_DOUBLE(_val) \
+  (&((JsonrpcMessagePutDouble) { .magic = {_JSONRPC_MESSAGE_PUT_DOUBLE_MAGIC_C}, .val = _val }))
+#define JSONRPC_MESSAGE_GET_DOUBLE(_valptr) \
+  (&((JsonrpcMessageGetDouble) { .magic = {_JSONRPC_MESSAGE_GET_DOUBLE_MAGIC_C}, .valptr = _valptr }))
+
+#define JSONRPC_MESSAGE_GET_ITER(_valptr) \
+  (&((JsonrpcMessageGetIter) { .magic = {_JSONRPC_MESSAGE_GET_ITER_MAGIC_C}, .iterptr = _valptr }))
+
+#define JSONRPC_MESSAGE_GET_DICT(_valptr) \
+  (&((JsonrpcMessageGetDict) { .magic = {_JSONRPC_MESSAGE_GET_DICT_MAGIC_C}, .dictptr = _valptr }))
+
+#define JSONRPC_MESSAGE_GET_VARIANT(_valptr) \
+  (&((JsonrpcMessageGetVariant) { .magic = {_JSONRPC_MESSAGE_GET_VARIANT_MAGIC_C}, .variantptr = _valptr }))
+
+GVariant *jsonrpc_message_new         (gpointer first_param, ...) G_GNUC_NULL_TERMINATED;
+gboolean  jsonrpc_message_parse       (GVariant *message, ...) G_GNUC_NULL_TERMINATED;
+gboolean  jsonrpc_message_parse_array (GVariantIter *iter, ...) G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* JSONRPC_MESSAGE_H */
diff --git a/contrib/jsonrpc-glib/jsonrpc-output-stream.c b/contrib/jsonrpc-glib/jsonrpc-output-stream.c
index efbfb61..6d59585 100644
--- a/contrib/jsonrpc-glib/jsonrpc-output-stream.c
+++ b/contrib/jsonrpc-glib/jsonrpc-output-stream.c
@@ -23,9 +23,33 @@
 #include "jsonrpc-output-stream.h"
 #include "jsonrpc-version.h"
 
+/**
+ * SECTION:jsonrpc-output-stream
+ * @title: #JsonrpcOutputStream
+ * @short_description: A JSONRPC output stream
+ *
+ * The #JsonrpcOutputStream is resonsible for serializing messages onto
+ * the underlying stream.
+ *
+ * Optionally, if jsonrpc_output_stream_set_use_gvariant() has been called,
+ * the messages will be encoded directly in the #GVariant format instead of
+ * JSON along with setting a "Content-Type" header to "application/gvariant".
+ * This is useful for situations where you control both sides of the RPC server
+ * using jsonrpc-glib as you can reduce the overhead of parsing JSON nodes due
+ * to #GVariant not requiring parsing or allocation overhead to the same degree
+ * as JSON.
+ *
+ * For example, if you need a large message, which is encoded in JSON, you need
+ * to decode the entire message up front which avoids performing lazy operations.
+ * When using GVariant encoding, you have a single allocation created for the
+ * #GVariant which means you reduce the memory pressure caused by lots of small
+ * allocations.
+ */
+
 typedef struct
 {
   GQueue queue;
+  guint  use_gvariant : 1;
 } JsonrpcOutputStreamPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (JsonrpcOutputStream, jsonrpc_output_stream, G_TYPE_DATA_OUTPUT_STREAM)
@@ -34,9 +58,54 @@ static void jsonrpc_output_stream_write_message_async_cb (GObject      *object,
                                                           GAsyncResult *result,
                                                           gpointer      user_data);
 
+enum {
+  PROP_0,
+  PROP_USE_GVARIANT,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
 static gboolean jsonrpc_output_stream_debug;
 
 static void
+jsonrpc_output_stream_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  JsonrpcOutputStream *self = JSONRPC_OUTPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_USE_GVARIANT:
+      g_value_set_boolean (value, jsonrpc_output_stream_get_use_gvariant (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+jsonrpc_output_stream_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  JsonrpcOutputStream *self = JSONRPC_OUTPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_USE_GVARIANT:
+      jsonrpc_output_stream_set_use_gvariant (self, g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
 jsonrpc_output_stream_finalize (GObject *object)
 {
   JsonrpcOutputStream *self = (JsonrpcOutputStream *)object;
@@ -54,6 +123,17 @@ jsonrpc_output_stream_class_init (JsonrpcOutputStreamClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   object_class->finalize = jsonrpc_output_stream_finalize;
+  object_class->get_property = jsonrpc_output_stream_get_property;
+  object_class->set_property = jsonrpc_output_stream_set_property;
+
+  properties [PROP_USE_GVARIANT] =
+    g_param_spec_boolean ("use-gvariant",
+                         "Use GVariant",
+                         "If GVariant encoding should be used",
+                         FALSE,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
 
   jsonrpc_output_stream_debug = !!g_getenv ("JSONRPC_DEBUG");
 }
@@ -68,44 +148,61 @@ jsonrpc_output_stream_init (JsonrpcOutputStream *self)
 
 static GBytes *
 jsonrpc_output_stream_create_bytes (JsonrpcOutputStream  *self,
-                                    JsonNode             *node,
+                                    GVariant             *message,
                                     GError              **error)
 {
-  g_autofree gchar *str = NULL;
-  GString *message;
+  JsonrpcOutputStreamPrivate *priv = jsonrpc_output_stream_get_instance_private (self);
+  g_autoptr(GByteArray) buffer = NULL;
+  g_autofree gchar *message_freeme = NULL;
+  gconstpointer message_data = NULL;
+  gsize message_len = 0;
+  gchar header[256];
   gsize len;
 
   g_assert (JSONRPC_IS_OUTPUT_STREAM (self));
-  g_assert (node != NULL);
+  g_assert (message != NULL);
 
-  if (!JSON_NODE_HOLDS_OBJECT (node) && !JSON_NODE_HOLDS_ARRAY (node))
-    {
-      g_set_error (error,
-                   G_IO_ERROR,
-                   G_IO_ERROR_INVAL,
-                   "node must be an array or object");
-      return FALSE;
-    }
-
-  str = json_to_string (node, FALSE);
-  len = strlen (str);
+  buffer = g_byte_array_sized_new (g_variant_get_size (message) + 128);
 
+#if 0
   if G_UNLIKELY (jsonrpc_output_stream_debug)
     g_message (">>> %s", str);
+#endif
 
-  /*
-   * Try to allocate our buffer in a single shot. Sadly we can't serialize
-   * JsonNode directly into a GString or we could remove the double
-   * allocation going on here.
-   */
-  message = g_string_sized_new (len + 32);
+  if (priv->use_gvariant)
+    {
+      message_data = g_variant_get_data (message);
+      message_len = g_variant_get_size (message);
+    }
+  else
+    {
+      message_freeme = json_gvariant_serialize_data (message, &message_len);
+      message_data = message_freeme;
+    }
+
+  /* Add Content-Length header */
+  len = g_snprintf (header, sizeof header, "Content-Length: %"G_GSIZE_FORMAT"\r\n", message_len);
+  g_byte_array_append (buffer, (const guint8 *)header, len);
+
+  if (priv->use_gvariant)
+    {
+      /* Add Content-Type header */
+      len = g_snprintf (header, sizeof header, "Content-Type: application/%s\r\n",
+                        priv->use_gvariant ? "gvariant" : "json");
+      g_byte_array_append (buffer, (const guint8 *)header, len);
+
+      /* Add our GVariantType for the peer to decode */
+      len = g_snprintf (header, sizeof header, "X-GVariant-Type: %s\r\n",
+                        (const gchar *)g_variant_get_type_string (message));
+      g_byte_array_append (buffer, (const guint8 *)header, len);
+    }
 
-  g_string_append_printf (message, "Content-Length: %"G_GSIZE_FORMAT"\r\n\r\n", len);
-  g_string_append_len (message, str, len);
+  g_byte_array_append (buffer, (const guint8 *)"\r\n", 2);
 
-  len = message->len;
+  /* Add serialized message data */
+  g_byte_array_append (buffer, (const guint8 *)message_data, message_len);
 
-  return g_bytes_new_take (g_string_free (message, FALSE), len);
+  return g_byte_array_free_to_bytes (g_steal_pointer (&buffer));
 }
 
 JsonrpcOutputStream *
@@ -179,7 +276,7 @@ jsonrpc_output_stream_write_message_async_cb (GObject      *object,
                                               gpointer      user_data)
 {
   GOutputStream *stream = (GOutputStream *)object;
-  g_autoptr(JsonrpcOutputStream) self = NULL;
+  JsonrpcOutputStream *self;
   g_autoptr(GError) error = NULL;
   g_autoptr(GTask) task = user_data;
   GBytes *bytes;
@@ -188,8 +285,7 @@ jsonrpc_output_stream_write_message_async_cb (GObject      *object,
   g_assert (G_IS_OUTPUT_STREAM (stream));
   g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (G_IS_TASK (task));
-
-  self = g_object_ref (g_task_get_source_object (task));
+  self = g_task_get_source_object (task);
   g_assert (JSONRPC_IS_OUTPUT_STREAM (self));
 
   if (!g_output_stream_write_all_finish (stream, result, &n_written, &error))
@@ -217,7 +313,7 @@ jsonrpc_output_stream_write_message_async_cb (GObject      *object,
 
 void
 jsonrpc_output_stream_write_message_async (JsonrpcOutputStream *self,
-                                           JsonNode            *node,
+                                           GVariant            *message,
                                            GCancellable        *cancellable,
                                            GAsyncReadyCallback  callback,
                                            gpointer             user_data)
@@ -228,13 +324,13 @@ jsonrpc_output_stream_write_message_async (JsonrpcOutputStream *self,
   g_autoptr(GError) error = NULL;
 
   g_return_if_fail (JSONRPC_IS_OUTPUT_STREAM (self));
-  g_return_if_fail (node != NULL);
+  g_return_if_fail (message != NULL);
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, jsonrpc_output_stream_write_message_async);
 
-  if (NULL == (bytes = jsonrpc_output_stream_create_bytes (self, node, &error)))
+  if (NULL == (bytes = jsonrpc_output_stream_create_bytes (self, message, &error)))
     {
       g_task_return_error (task, g_steal_pointer (&error));
       return;
@@ -277,7 +373,7 @@ jsonrpc_output_stream_write_message_sync_cb (GObject      *object,
 
 gboolean
 jsonrpc_output_stream_write_message (JsonrpcOutputStream  *self,
-                                     JsonNode             *node,
+                                     GVariant             *message,
                                      GCancellable         *cancellable,
                                      GError              **error)
 {
@@ -285,7 +381,7 @@ jsonrpc_output_stream_write_message (JsonrpcOutputStream  *self,
   g_autoptr(GMainContext) main_context = NULL;
 
   g_return_val_if_fail (JSONRPC_IS_OUTPUT_STREAM (self), FALSE);
-  g_return_val_if_fail (node != NULL, FALSE);
+  g_return_val_if_fail (message != NULL, FALSE);
   g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
 
   main_context = g_main_context_ref_thread_default ();
@@ -294,7 +390,7 @@ jsonrpc_output_stream_write_message (JsonrpcOutputStream  *self,
   g_task_set_source_tag (task, jsonrpc_output_stream_write_message);
 
   jsonrpc_output_stream_write_message_async (self,
-                                             node,
+                                             message,
                                              cancellable,
                                              jsonrpc_output_stream_write_message_sync_cb,
                                              task);
@@ -304,3 +400,30 @@ jsonrpc_output_stream_write_message (JsonrpcOutputStream  *self,
 
   return g_task_propagate_boolean (task, error);
 }
+
+gboolean
+jsonrpc_output_stream_get_use_gvariant (JsonrpcOutputStream *self)
+{
+  JsonrpcOutputStreamPrivate *priv = jsonrpc_output_stream_get_instance_private (self);
+
+  g_return_val_if_fail (JSONRPC_IS_OUTPUT_STREAM (self), FALSE);
+
+  return priv->use_gvariant;
+}
+
+void
+jsonrpc_output_stream_set_use_gvariant (JsonrpcOutputStream *self,
+                                        gboolean             use_gvariant)
+{
+  JsonrpcOutputStreamPrivate *priv = jsonrpc_output_stream_get_instance_private (self);
+
+  g_return_if_fail (JSONRPC_IS_OUTPUT_STREAM (self));
+
+  use_gvariant = !!use_gvariant;
+
+  if (priv->use_gvariant != use_gvariant)
+    {
+      priv->use_gvariant = use_gvariant;
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USE_GVARIANT]);
+    }
+}
diff --git a/contrib/jsonrpc-glib/jsonrpc-output-stream.h b/contrib/jsonrpc-glib/jsonrpc-output-stream.h
index c4929cd..1d53ae5 100644
--- a/contrib/jsonrpc-glib/jsonrpc-output-stream.h
+++ b/contrib/jsonrpc-glib/jsonrpc-output-stream.h
@@ -47,12 +47,15 @@ struct _JsonrpcOutputStreamClass
 };
 
 JsonrpcOutputStream *jsonrpc_output_stream_new                  (GOutputStream        *base_stream);
+gboolean             jsonrpc_output_stream_get_use_gvariant     (JsonrpcOutputStream  *self);
+void                 jsonrpc_output_stream_set_use_gvariant     (JsonrpcOutputStream  *self,
+                                                                 gboolean              use_gvariant);
 gboolean             jsonrpc_output_stream_write_message        (JsonrpcOutputStream  *self,
-                                                                 JsonNode             *node,
+                                                                 GVariant             *message,
                                                                  GCancellable         *cancellable,
                                                                  GError              **error);
 void                 jsonrpc_output_stream_write_message_async  (JsonrpcOutputStream  *self,
-                                                                 JsonNode             *node,
+                                                                 GVariant             *message,
                                                                  GCancellable         *cancellable,
                                                                  GAsyncReadyCallback   callback,
                                                                  gpointer              user_data);
diff --git a/contrib/jsonrpc-glib/jsonrpc-server.c b/contrib/jsonrpc-glib/jsonrpc-server.c
index f809e5a..eaf69c8 100644
--- a/contrib/jsonrpc-glib/jsonrpc-server.c
+++ b/contrib/jsonrpc-glib/jsonrpc-server.c
@@ -65,8 +65,8 @@ jsonrpc_server_class_init (JsonrpcServerClass *klass)
                   4,
                   JSONRPC_TYPE_CLIENT,
                   G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
-                  JSON_TYPE_NODE,
-                  JSON_TYPE_NODE);
+                  G_TYPE_VARIANT,
+                  G_TYPE_VARIANT);
 
   signals [NOTIFICATION] =
     g_signal_new ("notification",
@@ -78,7 +78,7 @@ jsonrpc_server_class_init (JsonrpcServerClass *klass)
                   3,
                   JSONRPC_TYPE_CLIENT,
                   G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
-                  JSON_TYPE_NODE);
+                  G_TYPE_VARIANT);
 }
 
 static void
diff --git a/contrib/jsonrpc-glib/jsonrpc-server.h b/contrib/jsonrpc-glib/jsonrpc-server.h
index 82542da..d5ba8a0 100644
--- a/contrib/jsonrpc-glib/jsonrpc-server.h
+++ b/contrib/jsonrpc-glib/jsonrpc-server.h
@@ -19,7 +19,6 @@
 #ifndef JSONRPC_SERVER_H
 #define JSONRPC_SERVER_H
 
-#include <json-glib/json-glib.h>
 #include <gio/gio.h>
 
 #include "jsonrpc-client.h"
@@ -37,12 +36,12 @@ struct _JsonrpcServerClass
   gboolean (*handle_call)  (JsonrpcServer *self,
                             JsonrpcClient *client,
                             const gchar   *method,
-                            JsonNode      *id,
-                            JsonNode      *params);
+                            GVariant      *id,
+                            GVariant      *params);
   void     (*notification) (JsonrpcServer *self,
                             JsonrpcClient *client,
                             const gchar   *method,
-                            JsonNode      *params);
+                            GVariant      *params);
 
   gpointer _reserved1;
   gpointer _reserved2;
diff --git a/contrib/jsonrpc-glib/jsonrpc-version.h.in b/contrib/jsonrpc-glib/jsonrpc-version.h.in
index 07ae1df..1f0768a 100644
--- a/contrib/jsonrpc-glib/jsonrpc-version.h.in
+++ b/contrib/jsonrpc-glib/jsonrpc-version.h.in
@@ -25,7 +25,7 @@
 #endif
 
 /**
- * SECTION:jsonrpcversion
+ * SECTION:jsonrpc-version
  * @short_description: jsonrpc-glib version checking
  *
  * jsonrpc-glib provides macros to check the version of the library
diff --git a/contrib/jsonrpc-glib/test-message.c b/contrib/jsonrpc-glib/test-message.c
new file mode 100644
index 0000000..1acb170
--- /dev/null
+++ b/contrib/jsonrpc-glib/test-message.c
@@ -0,0 +1,162 @@
+#include "jsonrpc-message.h"
+
+static void
+test_basic (void)
+{
+  g_autoptr(GVariant) node = NULL;
+  const gchar *foo1 = NULL;
+  gint baz_baz_baz = 0;
+  gboolean r;
+
+  node = JSONRPC_MESSAGE_NEW (
+    "foo", "foo1",
+    "bar", "foo2",
+    "baz", "{",
+      "baz", "[", "{", "baz", JSONRPC_MESSAGE_PUT_INT32 (123), "}", "]",
+    "}"
+  );
+
+  g_assert (node != NULL);
+
+  r = JSONRPC_MESSAGE_PARSE (node,
+    "foo", JSONRPC_MESSAGE_GET_STRING (&foo1),
+    "baz", "{",
+      "baz", "[", "{", "baz", JSONRPC_MESSAGE_GET_INT32 (&baz_baz_baz), "}", "]",
+    "}"
+  );
+
+  g_assert_cmpstr (foo1, ==, "foo1");
+  g_assert_cmpint (baz_baz_baz, ==, 123);
+  g_assert_cmpint (r, ==, 1);
+}
+
+static void
+test_deep_array (void)
+{
+  g_autoptr(GVariant) node = NULL;
+  const gchar *abc = NULL;
+  const gchar *xyz = NULL;
+  gboolean r;
+
+  node = JSONRPC_MESSAGE_NEW ("foo", "[","[","[","[","[","[","[","[","[","[", "abc", "]", 
"]","]","]","]","]","]","]","]","]");
+  g_assert (node != NULL);
+
+  r = JSONRPC_MESSAGE_PARSE (node, "foo", "[","[","[","[","[","[","[","[","[","[", 
JSONRPC_MESSAGE_GET_STRING (&abc), "]", "]","]","]","]","]","]","]","]","]");
+  g_assert_cmpstr (abc, ==, "abc");
+  g_assert_cmpint (r, ==, 1);
+
+  g_clear_pointer (&node, g_variant_unref);
+
+  node = JSONRPC_MESSAGE_NEW ("foo", "[","[","[","[","[","[","[","[","[","{", "foo", "xyz", "}", 
"]","]","]","]","]","]","]","]","]");
+  g_assert (node != NULL);
+
+  r = JSONRPC_MESSAGE_PARSE (node, "foo", "[","[","[","[","[","[","[","[","[","{", "foo", 
JSONRPC_MESSAGE_GET_STRING (&xyz), "}", "]","]","]","]","]","]","]","]","]");
+  g_assert_cmpstr (xyz, ==, "xyz");
+  g_assert_cmpint (r, ==, 1);
+}
+
+static void
+test_extract_array (void)
+{
+  g_autoptr(GVariant) node = NULL;
+  g_autoptr(GVariant) ar123 = NULL;
+  gboolean r;
+  gint32 a=0, b=0, c=0;
+
+  node = JSONRPC_MESSAGE_NEW ("foo", "[", JSONRPC_MESSAGE_PUT_INT32 (1), JSONRPC_MESSAGE_PUT_INT32 (2), 
JSONRPC_MESSAGE_PUT_INT32 (3), "]");
+  g_assert (node != NULL);
+
+  r = JSONRPC_MESSAGE_PARSE (node, "foo", JSONRPC_MESSAGE_GET_VARIANT (&ar123));
+  g_assert (ar123 != NULL);
+  g_assert_cmpint (r, ==, 1);
+  g_assert_cmpint (3, ==, g_variant_n_children (ar123));
+
+  r = JSONRPC_MESSAGE_PARSE (node, "foo", "[", JSONRPC_MESSAGE_GET_INT32 (&a), JSONRPC_MESSAGE_GET_INT32 
(&b), JSONRPC_MESSAGE_GET_INT32 (&c), "]");
+  g_assert_cmpint (r, ==, 1);
+  g_assert_cmpint (a, ==, 1);
+  g_assert_cmpint (b, ==, 2);
+  g_assert_cmpint (c, ==, 3);
+}
+
+static void
+test_extract_object (void)
+{
+  g_autoptr(GVariant) node = NULL;
+  g_autoptr(GVariantDict) dict = NULL;
+  gboolean r;
+
+  node = JSONRPC_MESSAGE_NEW ("foo", "{", "bar", "[", JSONRPC_MESSAGE_PUT_INT32 (1), "two", 
JSONRPC_MESSAGE_PUT_INT32 (3), "]", "}");
+  g_assert (node != NULL);
+
+  r = JSONRPC_MESSAGE_PARSE (node, "foo", JSONRPC_MESSAGE_GET_DICT (&dict));
+  g_assert (dict != NULL);
+  g_assert (g_variant_dict_contains (dict, "bar"));
+  g_assert_cmpint (r, ==, TRUE);
+}
+
+static void
+test_extract_node (void)
+{
+  g_autoptr(GVariant) node = NULL;
+  g_autoptr(GVariant) ar = NULL;
+  gboolean r;
+
+  node = JSONRPC_MESSAGE_NEW ("foo", "{", "bar", "[", JSONRPC_MESSAGE_PUT_INT32 (1), "two", 
JSONRPC_MESSAGE_PUT_INT32 (3), "]", "}");
+  g_assert (node != NULL);
+
+  r = JSONRPC_MESSAGE_PARSE (node, "foo", "{", "bar", JSONRPC_MESSAGE_GET_VARIANT (&ar), "}");
+  g_assert (ar != NULL);
+  g_assert_cmpint (r, ==, TRUE);
+}
+
+static void
+test_paren (void)
+{
+  g_autoptr(GVariant) node = NULL;
+  const gchar *paren = "{";
+  const gchar *str = NULL;
+  gboolean r;
+
+  node = JSONRPC_MESSAGE_NEW ("foo", "{", "bar", "[", JSONRPC_MESSAGE_PUT_STRING (paren), "]", "}");
+  g_assert (node != NULL);
+
+  r = JSONRPC_MESSAGE_PARSE (node, "foo", "{", "bar", "[", JSONRPC_MESSAGE_GET_STRING (&str), "]", "}");
+  g_assert_cmpstr (str, ==, "{");
+  g_assert_cmpint (r, ==, TRUE);
+}
+
+static void
+test_array_toplevel (void)
+{
+  g_autoptr(GVariant) node = NULL;
+  g_autoptr(GVariantIter) iter = NULL;
+  const gchar *a = NULL;
+  const gchar *b = NULL;
+  gboolean r;
+
+  node = JSONRPC_MESSAGE_NEW ("foo", "[", "a", "b", "c", "d", "e", "]");
+  g_assert (node != NULL);
+
+  r = JSONRPC_MESSAGE_PARSE (node, "foo", JSONRPC_MESSAGE_GET_ITER (&iter));
+  g_assert_cmpint (r, ==, TRUE);
+  g_assert (iter != NULL);
+
+  r = JSONRPC_MESSAGE_PARSE_ARRAY (iter, JSONRPC_MESSAGE_GET_STRING (&a), JSONRPC_MESSAGE_GET_STRING (&b));
+  g_assert_cmpint (r, ==, TRUE);
+  g_assert_cmpstr (a, ==, "a");
+}
+
+gint
+main (gint argc,
+      gchar *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_add_func ("/Jcon/basic", test_basic);
+  g_test_add_func ("/Jcon/deep_array", test_deep_array);
+  g_test_add_func ("/Jcon/extract_array", test_extract_array);
+  g_test_add_func ("/Jcon/extract_object", test_extract_object);
+  g_test_add_func ("/Jcon/extract_node", test_extract_node);
+  g_test_add_func ("/Jcon/paren", test_paren);
+  g_test_add_func ("/Jcon/array_toplevel", test_array_toplevel);
+  return g_test_run ();
+}
diff --git a/contrib/jsonrpc-glib/test-server.c b/contrib/jsonrpc-glib/test-server.c
new file mode 100644
index 0000000..92711c8
--- /dev/null
+++ b/contrib/jsonrpc-glib/test-server.c
@@ -0,0 +1,179 @@
+/* test-server.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <fcntl.h>
+#include <gio/gio.h>
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixinputstream.h>
+#include <glib-unix.h>
+#include <jsonrpc-glib.h>
+#include <unistd.h>
+
+static void
+handle_notification (JsonrpcServer *server,
+                     JsonrpcClient *client,
+                     const gchar   *method,
+                     JsonNode      *params,
+                     gpointer       user_data)
+{
+  gint *count = user_data;
+
+  g_assert (JSONRPC_IS_SERVER (server));
+  g_assert (JSONRPC_IS_CLIENT (client));
+  g_assert (method != NULL);
+  g_assert (params != NULL);
+
+  (*count)++;
+}
+
+static gboolean
+handle_call (JsonrpcServer *server,
+             JsonrpcClient *client,
+             const gchar   *method,
+             GVariant      *id,
+             GVariant      *params,
+             gpointer       user_data)
+{
+  const gchar *rootPath = NULL;
+  g_autoptr(GError) error = NULL;
+  g_auto(GVariantDict) dict = { 0 };
+  gboolean r;
+
+  g_assert (id != NULL);
+  g_assert (params != NULL);
+  g_assert (g_variant_is_of_type (id, G_VARIANT_TYPE_INT64));
+  g_assert (g_variant_is_of_type (params, G_VARIANT_TYPE_VARDICT));
+
+  g_variant_dict_init (&dict, params);
+  r = g_variant_dict_lookup (&dict, "rootPath", "&s", &rootPath);
+  g_assert_cmpint (r, ==, TRUE);
+  g_assert_cmpstr (rootPath, ==, ".");
+  g_variant_dict_clear (&dict);
+
+  g_assert_cmpint (1, ==, g_variant_get_int64 (id));
+  g_assert_cmpstr (method, ==, "initialize");
+
+  g_variant_dict_init (&dict, NULL);
+  g_variant_dict_insert (&dict, "foo", "s", "bar");
+  r = jsonrpc_client_reply (client, id, g_variant_dict_end (&dict), NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (r, ==, TRUE);
+
+  return TRUE;
+}
+
+static void
+test_basic (gboolean use_gvariant)
+{
+  g_autoptr(JsonrpcServer) server = NULL;
+  g_autoptr(JsonrpcClient) client = NULL;
+  g_autoptr(GInputStream) input_a = NULL;
+  g_autoptr(GInputStream) input_b = NULL;
+  g_autoptr(GOutputStream) output_a = NULL;
+  g_autoptr(GOutputStream) output_b = NULL;
+  g_autoptr(GIOStream) stream_a = NULL;
+  g_autoptr(GIOStream) stream_b = NULL;
+  g_autoptr(GVariant) message = NULL;
+  g_autoptr(GVariant) return_value = NULL;
+  g_autoptr(GError) error = NULL;
+  GVariantDict dict;
+  gint pair_a[2];
+  gint pair_b[2];
+  gint count = 0;
+  gint r;
+
+  r = g_unix_open_pipe (pair_a, FD_CLOEXEC, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (r, ==, TRUE);
+
+  r = g_unix_open_pipe (pair_b, FD_CLOEXEC, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (r, ==, TRUE);
+
+  input_a = g_unix_input_stream_new (pair_a[0], TRUE);
+  input_b = g_unix_input_stream_new (pair_b[0], TRUE);
+  output_a = g_unix_output_stream_new (pair_a[1], TRUE);
+  output_b = g_unix_output_stream_new (pair_b[1], TRUE);
+
+  stream_a = g_simple_io_stream_new (input_a, output_b);
+  stream_b = g_simple_io_stream_new (input_b, output_a);
+
+  client = jsonrpc_client_new (stream_a);
+
+  /* Possibly upgrade connection to gvariant encoding */
+  jsonrpc_client_set_use_gvariant (client, use_gvariant);
+
+  server = jsonrpc_server_new ();
+  jsonrpc_server_accept_io_stream (server, stream_b);
+
+  g_signal_connect (server,
+                    "handle-call",
+                    G_CALLBACK (handle_call),
+                    NULL);
+
+  g_signal_connect (server,
+                    "notification",
+                    G_CALLBACK (handle_notification),
+                    &count);
+
+  r = jsonrpc_client_send_notification (client, "testNotification", NULL, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (r, ==, TRUE);
+
+  g_variant_dict_init (&dict, NULL);
+  g_variant_dict_insert (&dict, "rootPath", "s", ".");
+  message = g_variant_dict_end (&dict);
+
+  r = jsonrpc_client_call (client,
+                           "initialize",
+                           g_steal_pointer (&message),
+                           NULL,
+                           &return_value,
+                           &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (r, ==, TRUE);
+  g_assert (return_value != NULL);
+
+  g_assert_cmpint (count, ==, 1);
+}
+
+static void
+test_basic_json (void)
+{
+  test_basic (FALSE);
+}
+
+static void
+test_basic_gvariant (void)
+{
+  test_basic (TRUE);
+}
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_add_func ("/Jsonrpc/Server/json", test_basic_json);
+  g_test_add_func ("/Jsonrpc/Server/gvariant", test_basic_gvariant);
+  return g_test_run ();
+}
diff --git a/libide/langserv/ide-langserv-client.c b/libide/langserv/ide-langserv-client.c
index e4e1338..5493b0b 100644
--- a/libide/langserv/ide-langserv-client.c
+++ b/libide/langserv/ide-langserv-client.c
@@ -122,7 +122,7 @@ ide_langserv_client_buffer_saved (IdeLangservClient *self,
                                   IdeBuffer         *buffer,
                                   IdeBufferManager  *buffer_manager)
 {
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *uri = NULL;
 
   IDE_ENTRY;
@@ -136,9 +136,9 @@ ide_langserv_client_buffer_saved (IdeLangservClient *self,
 
   uri = ide_buffer_get_uri (buffer);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "textDocument", "{",
-      "uri", JCON_STRING (uri),
+      "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
     "}"
   );
 
@@ -161,7 +161,7 @@ ide_langserv_client_buffer_insert_text (IdeLangservClient *self,
                                         gint               len,
                                         IdeBuffer         *buffer)
 {
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *uri = NULL;
   g_autofree gchar *copy = NULL;
   gint line;
@@ -182,25 +182,25 @@ ide_langserv_client_buffer_insert_text (IdeLangservClient *self,
   line = gtk_text_iter_get_line (location);
   column = gtk_text_iter_get_line_offset (location);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "textDocument", "{",
-      "uri", JCON_STRING (uri),
-      "version", JCON_INT (version),
+      "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
+      "version", JSONRPC_MESSAGE_PUT_INT32 (version),
     "}",
     "contentChanges", "[",
       "{",
         "range", "{",
           "start", "{",
-            "line", JCON_INT (line),
-            "character", JCON_INT (column),
+            "line", JSONRPC_MESSAGE_PUT_INT32 (line),
+            "character", JSONRPC_MESSAGE_PUT_INT32 (column),
           "}",
           "end", "{",
-            "line", JCON_INT (line),
-            "character", JCON_INT (column),
+            "line", JSONRPC_MESSAGE_PUT_INT32 (line),
+            "character", JSONRPC_MESSAGE_PUT_INT32 (column),
           "}",
         "}",
-        "rangeLength", JCON_INT (0),
-        "text", JCON_STRING (copy),
+        "rangeLength", JSONRPC_MESSAGE_PUT_INT32 (0),
+        "text", JSONRPC_MESSAGE_PUT_STRING (copy),
       "}",
     "]");
 
@@ -218,7 +218,7 @@ ide_langserv_client_buffer_delete_range (IdeLangservClient *self,
                                          IdeBuffer         *buffer)
 {
 
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *uri = NULL;
   struct {
     gint line;
@@ -245,24 +245,24 @@ ide_langserv_client_buffer_delete_range (IdeLangservClient *self,
 
   length = gtk_text_iter_get_offset (end_iter) - gtk_text_iter_get_offset (begin_iter);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "textDocument", "{",
-      "uri", JCON_STRING (uri),
-      "version", JCON_INT (version),
+      "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
+      "version", JSONRPC_MESSAGE_PUT_INT32 (version),
     "}",
     "contentChanges", "[",
       "{",
         "range", "{",
           "start", "{",
-            "line", JCON_INT (begin.line),
-            "character", JCON_INT (begin.column),
+            "line", JSONRPC_MESSAGE_PUT_INT32 (begin.line),
+            "character", JSONRPC_MESSAGE_PUT_INT32 (begin.column),
           "}",
           "end", "{",
-            "line", JCON_INT (end.line),
-            "character", JCON_INT (end.column),
+            "line", JSONRPC_MESSAGE_PUT_INT32 (end.line),
+            "character", JSONRPC_MESSAGE_PUT_INT32 (end.column),
           "}",
         "}",
-        "rangeLength", JCON_INT (length),
+        "rangeLength", JSONRPC_MESSAGE_PUT_INT32 (length),
         "text", "",
       "}",
     "]");
@@ -279,7 +279,7 @@ ide_langserv_client_buffer_loaded (IdeLangservClient *self,
                                    IdeBuffer         *buffer,
                                    IdeBufferManager  *buffer_manager)
 {
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *uri = NULL;
 
   IDE_ENTRY;
@@ -305,9 +305,9 @@ ide_langserv_client_buffer_loaded (IdeLangservClient *self,
 
   uri = ide_buffer_get_uri (buffer);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "textDocument", "{",
-      "uri", JCON_STRING (uri),
+      "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
     "}"
   );
 
@@ -324,7 +324,7 @@ ide_langserv_client_buffer_unloaded (IdeLangservClient *self,
                                      IdeBuffer         *buffer,
                                      IdeBufferManager  *buffer_manager)
 {
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *uri = NULL;
 
   IDE_ENTRY;
@@ -338,9 +338,9 @@ ide_langserv_client_buffer_unloaded (IdeLangservClient *self,
 
   uri = ide_buffer_get_uri (buffer);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "textDocument", "{",
-      "uri", JCON_STRING (uri),
+      "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
     "}"
   );
 
@@ -391,7 +391,7 @@ ide_langserv_client_project_file_trashed (IdeLangservClient *self,
                                           GFile             *file,
                                           IdeProject        *project)
 {
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *uri = NULL;
 
   IDE_ENTRY;
@@ -402,11 +402,11 @@ ide_langserv_client_project_file_trashed (IdeLangservClient *self,
 
   uri = g_file_get_uri (file);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "changes", "[",
       "{",
-        "uri", JCON_STRING (uri),
-        "type", JCON_INT (FILE_CHANGE_TYPE_DELETED),
+        "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
+        "type", JSONRPC_MESSAGE_PUT_INT32 (FILE_CHANGE_TYPE_DELETED),
       "}",
     "]"
   );
@@ -427,7 +427,7 @@ ide_langserv_client_project_file_renamed (IdeLangservClient *self,
                                           GFile             *dst,
                                           IdeProject        *project)
 {
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *src_uri = NULL;
   g_autofree gchar *dst_uri = NULL;
 
@@ -441,15 +441,15 @@ ide_langserv_client_project_file_renamed (IdeLangservClient *self,
   src_uri = g_file_get_uri (src);
   dst_uri = g_file_get_uri (dst);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "changes", "["
       "{",
-        "uri", JCON_STRING (src_uri),
-        "type", JCON_INT (FILE_CHANGE_TYPE_DELETED),
+        "uri", JSONRPC_MESSAGE_PUT_STRING (src_uri),
+        "type", JSONRPC_MESSAGE_PUT_INT32 (FILE_CHANGE_TYPE_DELETED),
       "}",
       "{",
-        "uri", JCON_STRING (dst_uri),
-        "type", JCON_INT (FILE_CHANGE_TYPE_CREATED),
+        "uri", JSONRPC_MESSAGE_PUT_STRING (dst_uri),
+        "type", JSONRPC_MESSAGE_PUT_INT32 (FILE_CHANGE_TYPE_CREATED),
       "}",
     "]"
   );
@@ -467,28 +467,24 @@ ide_langserv_client_project_file_renamed (IdeLangservClient *self,
 static IdeDiagnostics *
 ide_langserv_client_translate_diagnostics (IdeLangservClient *self,
                                            IdeFile           *file,
-                                           JsonArray         *diagnostics)
+                                           GVariantIter      *diagnostics)
 {
   g_autoptr(GPtrArray) ar = NULL;
-  guint length;
+  GVariant *value;
 
   g_assert (IDE_IS_LANGSERV_CLIENT (self));
   g_assert (diagnostics != NULL);
 
-  length = json_array_get_length (diagnostics);
+  ar = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_diagnostic_unref);
 
-  ar = g_ptr_array_sized_new (length);
-  g_ptr_array_set_free_func (ar, (GDestroyNotify)ide_diagnostic_unref);
-
-  for (guint i = 0; i < length; i++)
+  while (g_variant_iter_loop (diagnostics, "v", &value))
     {
-      JsonNode *node = json_array_get_element (diagnostics, i);
       g_autoptr(IdeSourceLocation) begin_loc = NULL;
       g_autoptr(IdeSourceLocation) end_loc = NULL;
       g_autoptr(IdeDiagnostic) diag = NULL;
+      g_autoptr(GVariant) range = NULL;
       const gchar *message = NULL;
       const gchar *source = NULL;
-      JsonNode *range = NULL;
       gint severity = 0;
       gboolean success;
       struct {
@@ -497,24 +493,24 @@ ide_langserv_client_translate_diagnostics (IdeLangservClient *self,
       } begin, end;
 
       /* Mandatory fields */
-      if (!JCON_EXTRACT (node,
-                         "range", JCONE_NODE (range),
-                         "message", JCONE_STRING (message)))
+      if (!JSONRPC_MESSAGE_PARSE (value,
+                                  "range", JSONRPC_MESSAGE_GET_VARIANT (&range),
+                                  "message", JSONRPC_MESSAGE_GET_STRING (&message)))
         continue;
 
       /* Optional Fields */
-      JCON_EXTRACT (node, "severity", JCONE_INT (severity));
-      JCON_EXTRACT (node, "source", JCONE_STRING (source));
+      JSONRPC_MESSAGE_PARSE (value, "severity", JSONRPC_MESSAGE_GET_INT32 (&severity));
+      JSONRPC_MESSAGE_PARSE (value, "source", JSONRPC_MESSAGE_GET_STRING (&source));
 
       /* Extract location information */
-      success = JCON_EXTRACT (range,
+      success = JSONRPC_MESSAGE_PARSE (range,
         "start", "{",
-          "line", JCONE_INT (begin.line),
-          "character", JCONE_INT (begin.column),
+          "line", JSONRPC_MESSAGE_GET_INT32 (&begin.line),
+          "character", JSONRPC_MESSAGE_GET_INT32 (&begin.column),
         "}",
         "end", "{",
-          "line", JCONE_INT (end.line),
-          "character", JCONE_INT (end.column),
+          "line", JSONRPC_MESSAGE_GET_INT32 (&end.line),
+          "character", JSONRPC_MESSAGE_GET_INT32 (&end.column),
         "}"
       );
 
@@ -552,10 +548,10 @@ ide_langserv_client_translate_diagnostics (IdeLangservClient *self,
 
 static void
 ide_langserv_client_text_document_publish_diagnostics (IdeLangservClient *self,
-                                                       JsonNode          *params)
+                                                       GVariant          *params)
 {
   IdeLangservClientPrivate *priv = ide_langserv_client_get_instance_private (self);
-  JsonArray *json_diagnostics = NULL;
+  g_autoptr(GVariantIter) json_diagnostics = NULL;
   const gchar *uri = NULL;
   gboolean success;
 
@@ -564,9 +560,9 @@ ide_langserv_client_text_document_publish_diagnostics (IdeLangservClient *self,
   g_assert (IDE_IS_LANGSERV_CLIENT (self));
   g_assert (params != NULL);
 
-  success = JCON_EXTRACT (params,
-    "uri", JCONE_STRING (uri),
-    "diagnostics", JCONE_ARRAY (json_diagnostics)
+  success = JSONRPC_MESSAGE_PARSE (params,
+    "uri", JSONRPC_MESSAGE_GET_STRING (&uri),
+    "diagnostics", JSONRPC_MESSAGE_GET_ITER (&json_diagnostics)
   );
 
   if (success)
@@ -604,7 +600,7 @@ ide_langserv_client_text_document_publish_diagnostics (IdeLangservClient *self,
 static void
 ide_langserv_client_real_notification (IdeLangservClient *self,
                                        const gchar       *method,
-                                       JsonNode          *params)
+                                       GVariant          *params)
 {
   IDE_ENTRY;
 
@@ -621,7 +617,7 @@ ide_langserv_client_real_notification (IdeLangservClient *self,
 static void
 ide_langserv_client_send_notification (IdeLangservClient *self,
                                        const gchar       *method,
-                                       JsonNode          *params,
+                                       GVariant          *params,
                                        JsonrpcClient     *rpc_client)
 {
   GQuark detail;
@@ -753,7 +749,7 @@ ide_langserv_client_class_init (IdeLangservClientClass *klass)
                   G_TYPE_NONE,
                   2,
                   G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
-                  JSON_TYPE_NODE);
+                  G_TYPE_VARIANT);
 
   signals [SUPPORTS_LANGUAGE] =
     g_signal_new ("supports-language",
@@ -842,7 +838,7 @@ ide_langserv_client_initialize_cb (GObject      *object,
   JsonrpcClient *rpc_client = (JsonrpcClient *)object;
   g_autoptr(IdeLangservClient) self = user_data;
   IdeLangservClientPrivate *priv = ide_langserv_client_get_instance_private (self);
-  g_autoptr(JsonNode) reply = NULL;
+  g_autoptr(GVariant) reply = NULL;
   g_autoptr(GError) error = NULL;
   IdeBufferManager *buffer_manager;
   IdeProject *project;
@@ -896,7 +892,7 @@ void
 ide_langserv_client_start (IdeLangservClient *self)
 {
   IdeLangservClientPrivate *priv = ide_langserv_client_get_instance_private (self);
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *root_path = NULL;
   IdeContext *context;
   IdeVcs *vcs;
@@ -933,9 +929,9 @@ ide_langserv_client_start (IdeLangservClient *self)
    * also start our read loop.
    */
 
-  params = JCON_NEW (
-    "processId", JCON_INT (getpid ()),
-    "rootPath", JCON_STRING (root_path),
+  params = JSONRPC_MESSAGE_NEW (
+    "processId", JSONRPC_MESSAGE_PUT_INT32 (getpid ()),
+    "rootPath", JSONRPC_MESSAGE_PUT_STRING (root_path),
     "capabilities", "{", "}"
   );
 
@@ -1000,7 +996,7 @@ ide_langserv_client_call_cb (GObject      *object,
                              gpointer      user_data)
 {
   JsonrpcClient *client = (JsonrpcClient *)object;
-  g_autoptr(JsonNode) return_value = NULL;
+  g_autoptr(GVariant) return_value = NULL;
   g_autoptr(GError) error = NULL;
   g_autoptr(GTask) task = user_data;
 
@@ -1015,7 +1011,7 @@ ide_langserv_client_call_cb (GObject      *object,
       return;
     }
 
-  g_task_return_pointer (task, g_steal_pointer (&return_value), (GDestroyNotify)json_node_unref);
+  g_task_return_pointer (task, g_steal_pointer (&return_value), (GDestroyNotify)g_variant_unref);
 
   IDE_EXIT;
 }
@@ -1024,7 +1020,7 @@ ide_langserv_client_call_cb (GObject      *object,
  * ide_langserv_client_call_async:
  * @self: An #IdeLangservClient
  * @method: the method to call
- * @params: (nullable) (transfer full): An #JsonNode or %NULL
+ * @params: (nullable) (transfer full): An #GVariant or %NULL
  * @cancellable: (nullable): A cancellable or %NULL
  * @callback: the callback to receive the result, or %NULL
  * @user_data: user data for @callback
@@ -1034,7 +1030,7 @@ ide_langserv_client_call_cb (GObject      *object,
 void
 ide_langserv_client_call_async (IdeLangservClient   *self,
                                 const gchar         *method,
-                                JsonNode            *params,
+                                GVariant            *params,
                                 GCancellable        *cancellable,
                                 GAsyncReadyCallback  callback,
                                 gpointer             user_data)
@@ -1069,10 +1065,10 @@ ide_langserv_client_call_async (IdeLangservClient   *self,
 gboolean
 ide_langserv_client_call_finish (IdeLangservClient  *self,
                                  GAsyncResult       *result,
-                                 JsonNode          **return_value,
+                                 GVariant          **return_value,
                                  GError            **error)
 {
-  g_autoptr(JsonNode) local_return_value = NULL;
+  g_autoptr(GVariant) local_return_value = NULL;
   gboolean ret;
 
   IDE_ENTRY;
@@ -1112,7 +1108,7 @@ ide_langserv_client_send_notification_cb (GObject      *object,
  * ide_langserv_client_send_notification_async:
  * @self: An #IdeLangservClient
  * @method: the method to notification
- * @params: (nullable) (transfer full): An #JsonNode or %NULL
+ * @params: (nullable) (transfer full): An #GVariant or %NULL
  * @cancellable: (nullable): A cancellable or %NULL
  * @notificationback: the notificationback to receive the result, or %NULL
  * @user_data: user data for @notificationback
@@ -1122,7 +1118,7 @@ ide_langserv_client_send_notification_cb (GObject      *object,
 void
 ide_langserv_client_send_notification_async (IdeLangservClient   *self,
                                              const gchar         *method,
-                                             JsonNode            *params,
+                                             GVariant            *params,
                                              GCancellable        *cancellable,
                                              GAsyncReadyCallback  notificationback,
                                              gpointer             user_data)
diff --git a/libide/langserv/ide-langserv-client.h b/libide/langserv/ide-langserv-client.h
index a57979e..1db624a 100644
--- a/libide/langserv/ide-langserv-client.h
+++ b/libide/langserv/ide-langserv-client.h
@@ -19,8 +19,6 @@
 #ifndef IDE_LANGSERV_CLIENT_H
 #define IDE_LANGSERV_CLIENT_H
 
-#include <json-glib/json-glib.h>
-
 #include "ide-object.h"
 
 G_BEGIN_DECLS
@@ -35,7 +33,7 @@ struct _IdeLangservClientClass
 
   void     (*notification)          (IdeLangservClient *self,
                                      const gchar       *method,
-                                     JsonNode          *params);
+                                     GVariant          *params);
   gboolean (*supports_language)     (IdeLangservClient *self,
                                      const gchar       *language_id);
   void     (*published_diagnostics) (IdeLangservClient *self,
@@ -52,40 +50,40 @@ struct _IdeLangservClientClass
   gpointer _reserved8;
 };
 
-IdeLangservClient *ide_langserv_client_new                    (IdeContext           *context,
-                                                               GIOStream            *io_stream);
-void               ide_langserv_client_add_language           (IdeLangservClient    *self,
-                                                               const gchar          *language_id);
-void               ide_langserv_client_start                  (IdeLangservClient    *self);
-void               ide_langserv_client_stop                   (IdeLangservClient    *self);
-void               ide_langserv_client_call_async             (IdeLangservClient    *self,
-                                                               const gchar          *method,
-                                                               JsonNode             *params,
-                                                               GCancellable         *cancellable,
-                                                               GAsyncReadyCallback   callback,
-                                                               gpointer              user_data);
-gboolean           ide_langserv_client_call_finish            (IdeLangservClient    *self,
-                                                               GAsyncResult         *result,
-                                                               JsonNode            **return_value,
-                                                               GError              **error);
+IdeLangservClient *ide_langserv_client_new                      (IdeContext           *context,
+                                                                 GIOStream            *io_stream);
+void               ide_langserv_client_add_language             (IdeLangservClient    *self,
+                                                                 const gchar          *language_id);
+void               ide_langserv_client_start                    (IdeLangservClient    *self);
+void               ide_langserv_client_stop                     (IdeLangservClient    *self);
+void               ide_langserv_client_call_async               (IdeLangservClient    *self,
+                                                                 const gchar          *method,
+                                                                 GVariant             *params,
+                                                                 GCancellable         *cancellable,
+                                                                 GAsyncReadyCallback   callback,
+                                                                 gpointer              user_data);
+gboolean           ide_langserv_client_call_finish              (IdeLangservClient    *self,
+                                                                 GAsyncResult         *result,
+                                                                 GVariant            **return_value,
+                                                                 GError              **error);
 void               ide_langserv_client_send_notification_async  (IdeLangservClient    *self,
                                                                  const gchar          *method,
-                                                                 JsonNode             *params,
+                                                                 GVariant             *params,
                                                                  GCancellable         *cancellable,
                                                                  GAsyncReadyCallback   notificationback,
                                                                  gpointer              user_data);
 gboolean           ide_langserv_client_send_notification_finish (IdeLangservClient    *self,
                                                                  GAsyncResult         *result,
                                                                  GError              **error);
-void               ide_langserv_client_get_diagnostics_async  (IdeLangservClient    *self,
-                                                               GFile                *file,
-                                                               GCancellable         *cancellable,
-                                                               GAsyncReadyCallback   callback,
-                                                               gpointer              user_data);
-gboolean           ide_langserv_client_get_diagnostics_finish (IdeLangservClient    *self,
-                                                               GAsyncResult         *result,
-                                                               IdeDiagnostics      **diagnostics,
-                                                               GError              **error);
+void               ide_langserv_client_get_diagnostics_async    (IdeLangservClient    *self,
+                                                                 GFile                *file,
+                                                                 GCancellable         *cancellable,
+                                                                 GAsyncReadyCallback   callback,
+                                                                 gpointer              user_data);
+gboolean           ide_langserv_client_get_diagnostics_finish   (IdeLangservClient    *self,
+                                                                 GAsyncResult         *result,
+                                                                 IdeDiagnostics      **diagnostics,
+                                                                 GError              **error);
 
 G_END_DECLS
 
diff --git a/libide/langserv/ide-langserv-completion-provider.c 
b/libide/langserv/ide-langserv-completion-provider.c
index 3fa5ac7..45c2f6b 100644
--- a/libide/langserv/ide-langserv-completion-provider.c
+++ b/libide/langserv/ide-langserv-completion-provider.c
@@ -226,9 +226,11 @@ ide_langserv_completion_provider_complete_cb (GObject      *object,
 {
   IdeLangservClient *client = (IdeLangservClient *)object;
   g_autoptr(CompletionState) state = user_data;
+  g_autoptr(GVariant) return_value = NULL;
   g_autoptr(GError) error = NULL;
-  g_autoptr(JsonNode) return_value = NULL;
+  GVariant *node;
   GList *list = NULL;
+  GVariantIter iter;
 
   IDE_ENTRY;
 
@@ -250,41 +252,39 @@ ide_langserv_completion_provider_complete_cb (GObject      *object,
    *       the other completion result work we've done.
    */
 
-  if (JSON_NODE_HOLDS_ARRAY (return_value))
-    {
-      JsonArray *array = json_node_get_array (return_value);
-      guint length = json_array_get_length (array);
+  g_variant_iter_init (&iter, return_value);
 
-      for (guint i = length; i > 0; i--)
+  while (g_variant_iter_loop (&iter, "v", &node))
+    {
+      g_autoptr(GtkSourceCompletionItem) item = NULL;
+      g_autofree gchar *full_label = NULL;
+      const gchar *label;
+      const gchar *detail;
+      gboolean success;
+
+      success = JSONRPC_MESSAGE_PARSE (node,
+        "label", JSONRPC_MESSAGE_GET_STRING (&label),
+        "detail", JSONRPC_MESSAGE_GET_STRING (&detail)
+      );
+
+      if (!success)
         {
-          JsonNode *node = json_array_get_element (array, i - 1);
-          g_autoptr(GtkSourceCompletionItem) item = NULL;
-          g_autofree gchar *full_label = NULL;
-          const gchar *label;
-          const gchar *detail;
-          gboolean success;
-
-          success = JCON_EXTRACT (node,
-            "label", JCONE_STRING (label),
-            "detail", JCONE_STRING (detail)
-          );
-
-          if (!success)
-            continue;
-
-          if (label != NULL && detail != NULL)
-            full_label = g_strdup_printf ("%s : %s", label, detail);
-          else
-            full_label = g_strdup (label);
-
-          //item = gtk_source_completion_item_new (full_label, label, NULL, NULL);
-          item = g_object_new (GTK_SOURCE_TYPE_COMPLETION_ITEM,
-                               "label", full_label,
-                               "text", label,
-                               NULL);
-
-          list = g_list_prepend (list, g_steal_pointer (&item));
+          IDE_TRACE_MSG ("Failed to extract completion item from node");
+          continue;
         }
+
+      if (label != NULL && detail != NULL)
+        full_label = g_strdup_printf ("%s : %s", label, detail);
+      else
+        full_label = g_strdup (label);
+
+      //item = gtk_source_completion_item_new (full_label, label, NULL, NULL);
+      item = g_object_new (GTK_SOURCE_TYPE_COMPLETION_ITEM,
+                           "label", full_label,
+                           "text", label,
+                           NULL);
+
+      list = g_list_prepend (list, g_steal_pointer (&item));
     }
 
 failure:
@@ -304,7 +304,7 @@ ide_langserv_completion_provider_populate (GtkSourceCompletionProvider *provider
 {
   IdeLangservCompletionProvider *self = (IdeLangservCompletionProvider *)provider;
   IdeLangservCompletionProviderPrivate *priv = ide_langserv_completion_provider_get_instance_private (self);
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autoptr(GCancellable) cancellable = NULL;
   g_autoptr(CompletionState) state = NULL;
   g_autofree gchar *uri = NULL;
@@ -333,13 +333,13 @@ ide_langserv_completion_provider_populate (GtkSourceCompletionProvider *provider
   line = gtk_text_iter_get_line (&iter);
   column = gtk_text_iter_get_line_offset (&iter);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "textDocument", "{",
-      "uri", JCON_STRING (uri),
+      "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
     "}",
     "position", "{",
-      "line", JCON_INT (line),
-      "character", JCON_INT (column),
+      "line", JSONRPC_MESSAGE_PUT_INT32 (line),
+      "character", JSONRPC_MESSAGE_PUT_INT32 (column),
     "}"
   );
 
diff --git a/libide/langserv/ide-langserv-highlighter.c b/libide/langserv/ide-langserv-highlighter.c
index 3d517c7..cdb6716 100644
--- a/libide/langserv/ide-langserv-highlighter.c
+++ b/libide/langserv/ide-langserv-highlighter.c
@@ -99,9 +99,9 @@ ide_langserv_highlighter_document_symbol_cb (GObject      *object,
   IdeLangservClient *client = (IdeLangservClient *)object;
   g_autoptr(IdeLangservHighlighter) self = user_data;
   IdeLangservHighlighterPrivate *priv = ide_langserv_highlighter_get_instance_private (self);
-  g_autoptr(JsonNode) return_value = NULL;
+  g_autoptr(GVariant) return_value = NULL;
   g_autoptr(GError) error = NULL;
-  JsonArray *ar;
+  GVariantIter iter;
 
   IDE_ENTRY;
 
@@ -117,31 +117,30 @@ ide_langserv_highlighter_document_symbol_cb (GObject      *object,
       IDE_EXIT;
     }
 
-  /*
-   * TODO: We should get the tag to have the proper name based
-   *       on the type.
-   */
+  /* TODO: We should get the tag to have the proper name based on the type. */
 
-  if (JSON_NODE_HOLDS_ARRAY (return_value) && NULL != (ar = json_node_get_array (return_value)))
+  if (g_variant_iter_init (&iter, return_value))
     {
       g_autoptr(IdeHighlightIndex) index = ide_highlight_index_new ();
-      guint length = json_array_get_length (ar);
+      GVariant *member = NULL;
 
-      for (guint i = 0; i < length; i++)
+      while (g_variant_iter_loop (&iter, "v", &member))
         {
-          JsonNode *member = json_array_get_element (ar, i);
           const gchar *name = NULL;
           const gchar *tag;
           gboolean success;
           gint kind = 0;
 
-          success = JCON_EXTRACT (member,
-            "name", JCONE_STRING (name),
-            "kind", JCONE_INT (kind)
+          success = JSONRPC_MESSAGE_PARSE (member,
+            "name", JSONRPC_MESSAGE_GET_STRING (&name),
+            "kind", JSONRPC_MESSAGE_GET_INT32 (&kind)
           );
 
           if (!success)
-            continue;
+            {
+              IDE_TRACE_MSG ("Failed to unwrap name and kind from symbol");
+              continue;
+            }
 
           switch (kind)
             {
@@ -160,7 +159,6 @@ ide_langserv_highlighter_document_symbol_cb (GObject      *object,
               tag = "def:type";
               break;
 
-
             case 14:  /* CONSTANT */
               tag = "def:constant";
               break;
@@ -200,16 +198,16 @@ ide_langserv_highlighter_update_symbols (gpointer data)
 
   if (priv->client != NULL && priv->engine != NULL)
     {
-      g_autoptr(JsonNode) params = NULL;
+      g_autoptr(GVariant) params = NULL;
       g_autofree gchar *uri = NULL;
       IdeBuffer *buffer;
 
       buffer = ide_highlight_engine_get_buffer (priv->engine);
       uri = ide_buffer_get_uri (buffer);
 
-      params = JCON_NEW (
+      params = JSONRPC_MESSAGE_NEW (
         "textDocument", "{",
-          "uri", JCON_STRING (uri),
+          "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
         "}"
       );
 
diff --git a/libide/langserv/ide-langserv-rename-provider.c b/libide/langserv/ide-langserv-rename-provider.c
index e70e62f..4432807 100644
--- a/libide/langserv/ide-langserv-rename-provider.c
+++ b/libide/langserv/ide-langserv-rename-provider.c
@@ -126,15 +126,14 @@ ide_langserv_rename_provider_rename_cb (GObject      *object,
 {
   IdeLangservClient *client = (IdeLangservClient *)object;
   IdeLangservRenameProvider *self;
-  g_autoptr(JsonNode) return_value = NULL;
+  g_autoptr(GVariant) return_value = NULL;
   g_autoptr(GError) error = NULL;
   g_autoptr(GTask) task = user_data;
   g_autoptr(GPtrArray) ret = NULL;
-  JsonObject *changes_by_uri = NULL;
-  JsonObjectIter iter;
-  const gchar *uri;
+  g_autoptr(GVariantIter) changes_by_uri = NULL;
   IdeContext *context;
-  JsonNode *changes;
+  const gchar *uri;
+  GVariant *changes;
 
   IDE_ENTRY;
 
@@ -150,31 +149,27 @@ ide_langserv_rename_provider_rename_cb (GObject      *object,
       IDE_EXIT;
     }
 
-  if (!JCON_EXTRACT (return_value, "changes", JCONE_OBJECT (changes_by_uri)))
+  if (!JSONRPC_MESSAGE_PARSE (return_value, "changes", JSONRPC_MESSAGE_GET_ITER (&changes_by_uri)))
     IDE_EXIT;
 
   context = ide_object_get_context (IDE_OBJECT (self));
 
   ret = g_ptr_array_new_with_free_func (g_object_unref);
 
-  json_object_iter_init (&iter, changes_by_uri);
-
-  while (json_object_iter_next (&iter, &uri, &changes))
+  while (g_variant_iter_loop (changes_by_uri, "{sv}", &uri, &changes))
     {
       g_autoptr(GFile) gfile = g_file_new_for_uri (uri);
       g_autoptr(IdeFile) ifile = ide_file_new (context, gfile);
-      JsonArray *array;
-      guint length;
+      GVariantIter changes_iter;
+      GVariant *change;
 
-      if (!JSON_NODE_HOLDS_ARRAY (changes))
+      if (!g_variant_is_container (changes))
         continue;
 
-      array = json_node_get_array (changes);
-      length = json_array_get_length (array);
+      g_variant_iter_init (&changes_iter, changes);
 
-      for (guint i = 0; i < length; i++)
+      while (g_variant_iter_loop (&changes_iter, "v", &change))
         {
-          JsonNode *change = json_array_get_element (array, i);
           g_autoptr(IdeSourceLocation) begin_location = NULL;
           g_autoptr(IdeSourceLocation) end_location = NULL;
           g_autoptr(IdeSourceRange) range = NULL;
@@ -186,22 +181,25 @@ ide_langserv_rename_provider_rename_cb (GObject      *object,
             gint column;
           } begin, end;
 
-          success = JCON_EXTRACT (change,
+          success = JSONRPC_MESSAGE_PARSE (change,
             "range", "{",
               "start", "{",
-                "line", JCONE_INT (begin.line),
-                "character", JCONE_INT (begin.column),
+                "line", JSONRPC_MESSAGE_GET_INT32 (&begin.line),
+                "character", JSONRPC_MESSAGE_GET_INT32 (&begin.column),
               "}",
               "end", "{",
-                "line", JCONE_INT (end.line),
-                "character", JCONE_INT (end.column),
+                "line", JSONRPC_MESSAGE_GET_INT32 (&end.line),
+                "character", JSONRPC_MESSAGE_GET_INT32 (&end.column),
               "}",
             "}",
-            "newText", JCONE_STRING (new_text)
+            "newText", JSONRPC_MESSAGE_GET_STRING (&new_text)
           );
 
           if (!success)
-            continue;
+            {
+              IDE_TRACE_MSG ("Failed to extract change from variant");
+              continue;
+            }
 
           begin_location = ide_source_location_new (ifile, begin.line, begin.column, 0);
           end_location = ide_source_location_new (ifile, end.line, end.column, 0);
@@ -232,7 +230,7 @@ ide_langserv_rename_provider_rename_async (IdeRenameProvider   *provider,
   IdeLangservRenameProvider *self = (IdeLangservRenameProvider *)provider;
   IdeLangservRenameProviderPrivate *priv = ide_langserv_rename_provider_get_instance_private (self);
   g_autoptr(GTask) task = NULL;
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *uri = NULL;
   IdeFile *ifile;
   GFile *gfile;
@@ -265,15 +263,15 @@ ide_langserv_rename_provider_rename_async (IdeRenameProvider   *provider,
   line = ide_source_location_get_line (location);
   column = ide_source_location_get_line_offset (location);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "textDocument", "{",
-      "uri", JCON_STRING (uri),
+      "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
     "}",
     "position", "{",
-      "line", JCON_INT (line),
-      "character", JCON_INT (column),
+      "line", JSONRPC_MESSAGE_PUT_INT32 (line),
+      "character", JSONRPC_MESSAGE_PUT_INT32 (column),
     "}",
-    "newName", JCON_STRING (new_name)
+    "newName", JSONRPC_MESSAGE_PUT_STRING (new_name)
   );
 
   ide_langserv_client_call_async (priv->client,
diff --git a/libide/langserv/ide-langserv-symbol-resolver.c b/libide/langserv/ide-langserv-symbol-resolver.c
index a4ebc85..172c196 100644
--- a/libide/langserv/ide-langserv-symbol-resolver.c
+++ b/libide/langserv/ide-langserv-symbol-resolver.c
@@ -161,12 +161,12 @@ ide_langserv_symbol_resolver_definition_cb (GObject      *object,
   IdeLangservSymbolResolver *self;
   g_autoptr(GTask) task = user_data;
   g_autoptr(GError) error = NULL;
-  g_autoptr(JsonNode) return_value = NULL;
+  g_autoptr(GVariant) return_value = NULL;
   g_autoptr(IdeSymbol) symbol = NULL;
   g_autoptr(IdeFile) ifile = NULL;
   g_autoptr(GFile) gfile = NULL;
   g_autoptr(IdeSourceLocation) location = NULL;
-  JsonNode *location_node = NULL;
+  g_autoptr(GVariant) location_node = NULL;
   const gchar *uri;
   gboolean success;
   struct {
@@ -191,27 +191,25 @@ ide_langserv_symbol_resolver_definition_cb (GObject      *object,
   /*
    * We can either get a Location or a Location[] from the peer. We only
    * care about the first node in this case, so extract Location[0] if
-   * we need to.
+   * we need to and unwrap the variant wrapper.
    */
-  if (JSON_NODE_HOLDS_ARRAY (return_value))
-    {
-      JsonArray *ar = json_node_get_array (return_value);
-      if (json_array_get_length (ar) > 0)
-        {
-          JsonNode *first = json_array_get_element (ar, 0);
-          if (JSON_NODE_HOLDS_OBJECT (first))
-            location_node = first;
-        }
-    }
-  else if (JSON_NODE_HOLDS_OBJECT (return_value))
-    location_node = return_value;
+  if (g_variant_is_of_type (return_value, G_VARIANT_TYPE ("av")) &&
+      g_variant_n_children (return_value) > 0)
+    g_variant_get_child (return_value, 0, "v", &location_node);
+  else
+    location_node = g_variant_get_child_value (return_value, 0);
 
   /*
    * If we failed to extract the appropriate node, we can just bail
    * as a failure.
    */
-  if (location_node == NULL)
+  if (location_node == NULL || !g_variant_is_of_type (location_node, G_VARIANT_TYPE ("a{sv}")))
     {
+      if (location_node != NULL)
+        {
+          IDE_TRACE_MSG ("textDocument/definition child is of type: %s",
+                         g_variant_get_type_string (location_node));
+        }
       g_task_return_new_error (task,
                                G_IO_ERROR,
                                G_IO_ERROR_INVALID_DATA,
@@ -219,18 +217,18 @@ ide_langserv_symbol_resolver_definition_cb (GObject      *object,
       IDE_EXIT;
     }
 
-  g_assert (JSON_NODE_HOLDS_OBJECT (location_node));
+  g_assert (g_variant_is_of_type (location_node, G_VARIANT_TYPE ("a{sv}")));
 
-  success = JCON_EXTRACT (location_node,
-    "uri", JCONE_STRING (uri),
+  success = JSONRPC_MESSAGE_PARSE (location_node,
+    "uri", JSONRPC_MESSAGE_GET_STRING (&uri),
     "range", "{",
       "start", "{",
-        "line", JCONE_INT (begin.line),
-        "character", JCONE_INT (begin.column),
+        "line", JSONRPC_MESSAGE_GET_INT32 (&begin.line),
+        "character", JSONRPC_MESSAGE_GET_INT32 (&begin.column),
       "}",
       "end", "{",
-        "line", JCONE_INT (end.line),
-        "character", JCONE_INT (end.column),
+        "line", JSONRPC_MESSAGE_GET_INT32 (&end.line),
+        "character", JSONRPC_MESSAGE_GET_INT32 (&end.column),
       "}",
     "}"
   );
@@ -267,7 +265,7 @@ ide_langserv_symbol_resolver_lookup_symbol_async (IdeSymbolResolver   *resolver,
   IdeLangservSymbolResolver *self = (IdeLangservSymbolResolver *)resolver;
   IdeLangservSymbolResolverPrivate *priv = ide_langserv_symbol_resolver_get_instance_private (self);
   g_autoptr(GTask) task = NULL;
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *uri = NULL;
   IdeFile *ifile;
   GFile *gfile;
@@ -307,13 +305,13 @@ ide_langserv_symbol_resolver_lookup_symbol_async (IdeSymbolResolver   *resolver,
   line = ide_source_location_get_line (location);
   column = ide_source_location_get_line_offset (location);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "textDocument", "{",
-      "uri", JCON_STRING (uri),
+      "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
     "}",
     "position", "{",
-      "line", JCON_INT (line),
-      "character", JCON_INT (column),
+      "line", JSONRPC_MESSAGE_PUT_INT32 (line),
+      "character", JSONRPC_MESSAGE_PUT_INT32 (column),
     "}"
   );
 
@@ -353,10 +351,10 @@ ide_langserv_symbol_resolver_document_symbol_cb (GObject      *object,
   g_autoptr(GTask) task = user_data;
   g_autoptr(IdeLangservSymbolTree) tree = NULL;
   g_autoptr(GError) error = NULL;
-  g_autoptr(JsonNode) return_value = NULL;
+  g_autoptr(GVariant) return_value = NULL;
   g_autoptr(GPtrArray) symbols = NULL;
-  JsonArray *array;
-  guint length;
+  GVariantIter iter;
+  GVariant *node;
 
   IDE_ENTRY;
 
@@ -369,8 +367,7 @@ ide_langserv_symbol_resolver_document_symbol_cb (GObject      *object,
       IDE_EXIT;
     }
 
-  if (!JSON_NODE_HOLDS_ARRAY (return_value) ||
-      NULL == (array = json_node_get_array (return_value)))
+  if (!g_variant_is_of_type (return_value, G_VARIANT_TYPE ("av")))
     {
       g_task_return_new_error (task,
                                G_IO_ERROR,
@@ -379,13 +376,12 @@ ide_langserv_symbol_resolver_document_symbol_cb (GObject      *object,
       IDE_EXIT;
     }
 
-  length = json_array_get_length (array);
-
   symbols = g_ptr_array_new_with_free_func (g_object_unref);
 
-  for (guint i = 0; i < length; i++)
+  g_variant_iter_init (&iter, return_value);
+
+  while (g_variant_iter_loop (&iter, "v", &node))
     {
-      JsonNode *node = json_array_get_element (array, i);
       g_autoptr(IdeLangservSymbolNode) symbol = NULL;
       g_autoptr(GFile) file = NULL;
       const gchar *name = NULL;
@@ -399,29 +395,32 @@ ide_langserv_symbol_resolver_document_symbol_cb (GObject      *object,
       } begin, end;
 
       /* Mandatory fields */
-      success = JCON_EXTRACT (node,
-        "name", JCONE_STRING (name),
-        "kind", JCONE_INT (kind),
+      success = JSONRPC_MESSAGE_PARSE (node,
+        "name", JSONRPC_MESSAGE_GET_STRING (&name),
+        "kind", JSONRPC_MESSAGE_GET_INT32 (&kind),
         "location", "{",
-          "uri", JCONE_STRING (uri),
+          "uri", JSONRPC_MESSAGE_GET_STRING (&uri),
           "range", "{",
             "start", "{",
-              "line", JCONE_INT (begin.line),
-              "character", JCONE_INT (begin.column),
+              "line", JSONRPC_MESSAGE_GET_INT32 (&begin.line),
+              "character", JSONRPC_MESSAGE_GET_INT32 (&begin.column),
             "}",
             "end", "{",
-              "line", JCONE_INT (end.line),
-              "character", JCONE_INT (end.column),
+              "line", JSONRPC_MESSAGE_GET_INT32 (&end.line),
+              "character", JSONRPC_MESSAGE_GET_INT32 (&end.column),
             "}",
           "}",
         "}"
       );
 
       if (!success)
-        continue;
+        {
+          IDE_TRACE_MSG ("Failed to parse reply from language server");
+          continue;
+        }
 
       /* Optional fields */
-      JCON_EXTRACT (node, "containerName", JCONE_STRING (container_name));
+      JSONRPC_MESSAGE_PARSE (node, "containerName", JSONRPC_MESSAGE_GET_STRING (&container_name));
 
       file = g_file_new_for_uri (uri);
 
@@ -450,7 +449,7 @@ ide_langserv_symbol_resolver_get_symbol_tree_async (IdeSymbolResolver   *resolve
   IdeLangservSymbolResolver *self = (IdeLangservSymbolResolver *)resolver;
   IdeLangservSymbolResolverPrivate *priv = ide_langserv_symbol_resolver_get_instance_private (self);
   g_autoptr(GTask) task = NULL;
-  g_autoptr(JsonNode) params = NULL;
+  g_autoptr(GVariant) params = NULL;
   g_autofree gchar *uri = NULL;
 
   IDE_ENTRY;
@@ -473,9 +472,9 @@ ide_langserv_symbol_resolver_get_symbol_tree_async (IdeSymbolResolver   *resolve
 
   uri = g_file_get_uri (file);
 
-  params = JCON_NEW (
+  params = JSONRPC_MESSAGE_NEW (
     "textDocument", "{",
-      "uri", JCON_STRING (uri),
+      "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
     "}"
   );
 
diff --git a/plugins/flatpak/gbp-flatpak-clone-widget.c b/plugins/flatpak/gbp-flatpak-clone-widget.c
index 27427e5..bbf1a67 100644
--- a/plugins/flatpak/gbp-flatpak-clone-widget.c
+++ b/plugins/flatpak/gbp-flatpak-clone-widget.c
@@ -17,6 +17,7 @@
  */
 
 #include <glib/gi18n.h>
+#include <json-glib/json-glib.h>
 #include <libgit2-glib/ggit.h>
 #include <ide.h>
 
diff --git a/plugins/flatpak/gbp-flatpak-configuration.c b/plugins/flatpak/gbp-flatpak-configuration.c
index 8d291af..5ab66ea 100644
--- a/plugins/flatpak/gbp-flatpak-configuration.c
+++ b/plugins/flatpak/gbp-flatpak-configuration.c
@@ -18,6 +18,8 @@
 
 #define G_LOG_DOMAIN "gbp-flatpak-configuration"
 
+#include <json-glib/json-glib.h>
+
 #include "gbp-flatpak-configuration.h"
 #include "gbp-flatpak-runtime.h"
 
diff --git a/src/main.c b/src/main.c
index 00f09aa..6b8f71a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -19,6 +19,7 @@
 #define G_LOG_DOMAIN "builder"
 
 #include <ide.h>
+#include <gtksourceview/gtksource.h>
 
 static gboolean
 verbose_cb (const gchar  *option_name,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e555c58..b4b5553 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -28,17 +28,6 @@ rg_libs =                                                       \
        $(RG_LIBS)                                              \
        $(NULL)
 
-jsonrpc_cflags =                                                \
-       $(DEBUG_CFLAGS)                                         \
-       $(JSONRPC_CFLAGS)                                       \
-       -I$(top_srcdir)/contrib/jsonrpc-glib                    \
-       $(NULL)
-
-jsonrpc_libs =                                                  \
-       $(top_builddir)/contrib/jsonrpc-glib/libjsonrpc-glib.la \
-       $(JSONRPC_LIBS)                                         \
-       $(NULL)
-
 search_cflags =                                                 \
        $(DEBUG_CFLAGS)                                         \
        $(SEARCH_CFLAGS)                                        \
@@ -269,12 +258,6 @@ test_egg_heap_CFLAGS = $(egg_cflags)
 test_egg_heap_LDADD = $(egg_libs)
 
 
-TESTS += test-jcon
-test_jcon_SOURCES = test-jcon.c
-test_jcon_CFLAGS = $(jsonrpc_cflags)
-test_jcon_LDADD = $(jsonrpc_libs)
-
-
 if ENABLE_TESTS
 noinst_PROGRAMS = $(TESTS) $(misc_programs)
 endif


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