diff --git a/block/crypto.c b/block/crypto.c
index cb8cbc2cc9..7eaa0571b5 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -193,17 +193,16 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
                             QemuOpts *opts,
                             Error **errp)
 {
-    OptsVisitor *ov;
+    Visitor *v;
     QCryptoBlockOpenOptions *ret = NULL;
     Error *local_err = NULL;
 
     ret = g_new0(QCryptoBlockOpenOptions, 1);
     ret->format = format;
 
-    ov = opts_visitor_new(opts);
+    v = opts_visitor_new(opts);
 
-    visit_start_struct(opts_get_visitor(ov),
-                       NULL, NULL, 0, &local_err);
+    visit_start_struct(v, NULL, NULL, 0, &local_err);
     if (local_err) {
         goto out;
     }
@@ -211,7 +210,7 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
     switch (format) {
     case Q_CRYPTO_BLOCK_FORMAT_LUKS:
         visit_type_QCryptoBlockOptionsLUKS_members(
-            opts_get_visitor(ov), &ret->u.luks, &local_err);
+            v, &ret->u.luks, &local_err);
         break;
 
     default:
@@ -219,10 +218,10 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
         break;
     }
     if (!local_err) {
-        visit_check_struct(opts_get_visitor(ov), &local_err);
+        visit_check_struct(v, &local_err);
     }
 
-    visit_end_struct(opts_get_visitor(ov));
+    visit_end_struct(v, NULL);
 
  out:
     if (local_err) {
@@ -230,7 +229,7 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
         qapi_free_QCryptoBlockOpenOptions(ret);
         ret = NULL;
     }
-    opts_visitor_cleanup(ov);
+    visit_free(v);
     return ret;
 }
 
@@ -240,17 +239,16 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
                               QemuOpts *opts,
                               Error **errp)
 {
-    OptsVisitor *ov;
+    Visitor *v;
     QCryptoBlockCreateOptions *ret = NULL;
     Error *local_err = NULL;
 
     ret = g_new0(QCryptoBlockCreateOptions, 1);
     ret->format = format;
 
-    ov = opts_visitor_new(opts);
+    v = opts_visitor_new(opts);
 
-    visit_start_struct(opts_get_visitor(ov),
-                       NULL, NULL, 0, &local_err);
+    visit_start_struct(v, NULL, NULL, 0, &local_err);
     if (local_err) {
         goto out;
     }
@@ -258,7 +256,7 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
     switch (format) {
     case Q_CRYPTO_BLOCK_FORMAT_LUKS:
         visit_type_QCryptoBlockCreateOptionsLUKS_members(
-            opts_get_visitor(ov), &ret->u.luks, &local_err);
+            v, &ret->u.luks, &local_err);
         break;
 
     default:
@@ -266,10 +264,10 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
         break;
     }
     if (!local_err) {
-        visit_check_struct(opts_get_visitor(ov), &local_err);
+        visit_check_struct(v, &local_err);
     }
 
-    visit_end_struct(opts_get_visitor(ov));
+    visit_end_struct(v, NULL);
 
  out:
     if (local_err) {
@@ -277,7 +275,7 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
         qapi_free_QCryptoBlockCreateOptions(ret);
         ret = NULL;
     }
-    opts_visitor_cleanup(ov);
+    visit_free(v);
     return ret;
 }
 
diff --git a/block/qapi.c b/block/qapi.c
index 5594f74d17..6f947e3e66 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -690,16 +690,15 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
 void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
                                    ImageInfoSpecific *info_spec)
 {
-    QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj, *data;
+    Visitor *v = qmp_output_visitor_new(&obj);
 
-    visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), NULL, &info_spec,
-                                 &error_abort);
-    obj = qmp_output_get_qobject(ov);
+    visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
+    visit_complete(v, &obj);
     assert(qobject_type(obj) == QTYPE_QDICT);
     data = qdict_get(qobject_to_qdict(obj), "data");
     dump_qobject(func_fprintf, f, 1, data);
-    qmp_output_visitor_cleanup(ov);
+    visit_free(v);
 }
 
 void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
diff --git a/blockdev.c b/blockdev.c
index 3a104a0aaa..0f8065c4a5 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3950,10 +3950,10 @@ out:
 
 void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
 {
-    QmpOutputVisitor *ov = qmp_output_visitor_new();
     BlockDriverState *bs;
     BlockBackend *blk = NULL;
     QObject *obj;
+    Visitor *v = qmp_output_visitor_new(&obj);
     QDict *qdict;
     Error *local_err = NULL;
 
@@ -3972,14 +3972,13 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
         }
     }
 
-    visit_type_BlockdevOptions(qmp_output_get_visitor(ov), NULL, &options,
-                               &local_err);
+    visit_type_BlockdevOptions(v, NULL, &options, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         goto fail;
     }
 
-    obj = qmp_output_get_qobject(ov);
+    visit_complete(v, &obj);
     qdict = qobject_to_qdict(obj);
 
     qdict_flatten(qdict);
@@ -4020,7 +4019,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
     }
 
 fail:
-    qmp_output_visitor_cleanup(ov);
+    visit_free(v);
 }
 
 void qmp_x_blockdev_del(bool has_id, const char *id,
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index eff207502f..48b0b31f2e 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -802,32 +802,28 @@ Example:
 
     void qapi_free_UserDefOne(UserDefOne *obj)
     {
-        QapiDeallocVisitor *qdv;
         Visitor *v;
 
         if (!obj) {
             return;
         }
 
-        qdv = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(qdv);
+        v = qapi_dealloc_visitor_new();
         visit_type_UserDefOne(v, NULL, &obj, NULL);
-        qapi_dealloc_visitor_cleanup(qdv);
+        visit_free(v);
     }
 
     void qapi_free_UserDefOneList(UserDefOneList *obj)
     {
-        QapiDeallocVisitor *qdv;
         Visitor *v;
 
         if (!obj) {
             return;
         }
 
-        qdv = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(qdv);
+        v = qapi_dealloc_visitor_new();
         visit_type_UserDefOneList(v, NULL, &obj, NULL);
-        qapi_dealloc_visitor_cleanup(qdv);
+        visit_free(v);
     }
 
 === scripts/qapi-visit.py ===
@@ -904,7 +900,7 @@ Example:
         }
         visit_check_struct(v, &err);
     out_obj:
-        visit_end_struct(v);
+        visit_end_struct(v, (void **)obj);
         if (err && visit_is_input(v)) {
             qapi_free_UserDefOne(*obj);
             *obj = NULL;
@@ -932,7 +928,7 @@ Example:
             }
         }
 
-        visit_end_list(v);
+        visit_end_list(v, (void **)obj);
         if (err && visit_is_input(v)) {
             qapi_free_UserDefOneList(*obj);
             *obj = NULL;
@@ -984,36 +980,28 @@ Example:
     static void qmp_marshal_output_UserDefOne(UserDefOne *ret_in, QObject **ret_out, Error **errp)
     {
         Error *err = NULL;
-        QmpOutputVisitor *qov = qmp_output_visitor_new();
-        QapiDeallocVisitor *qdv;
         Visitor *v;
 
-        v = qmp_output_get_visitor(qov);
+        v = qmp_output_visitor_new(ret_out);
         visit_type_UserDefOne(v, "unused", &ret_in, &err);
-        if (err) {
-            goto out;
+        if (!err) {
+            visit_complete(v, ret_out);
         }
-        *ret_out = qmp_output_get_qobject(qov);
-
-    out:
         error_propagate(errp, err);
-        qmp_output_visitor_cleanup(qov);
-        qdv = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(qdv);
+        visit_free(v);
+        v = qapi_dealloc_visitor_new();
         visit_type_UserDefOne(v, "unused", &ret_in, NULL);
-        qapi_dealloc_visitor_cleanup(qdv);
+        visit_free(v);
     }
 
     static void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp)
     {
         Error *err = NULL;
         UserDefOne *retval;
-        QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true);
-        QapiDeallocVisitor *qdv;
         Visitor *v;
         UserDefOneList *arg1 = NULL;
 
-        v = qmp_input_get_visitor(qiv);
+        v = qmp_input_visitor_new(QOBJECT(args), true);
         visit_start_struct(v, NULL, NULL, 0, &err);
         if (err) {
             goto out;
@@ -1022,7 +1010,7 @@ Example:
         if (!err) {
             visit_check_struct(v, &err);
         }
-        visit_end_struct(v);
+        visit_end_struct(v, NULL);
         if (err) {
             goto out;
         }
@@ -1036,13 +1024,12 @@ Example:
 
     out:
         error_propagate(errp, err);
-        qmp_input_visitor_cleanup(qiv);
-        qdv = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(qdv);
+        visit_free(v);
+        v = qapi_dealloc_visitor_new();
         visit_start_struct(v, NULL, NULL, 0, NULL);
         visit_type_UserDefOneList(v, "arg1", &arg1, NULL);
-        visit_end_struct(v);
-        qapi_dealloc_visitor_cleanup(qdv);
+        visit_end_struct(v, NULL);
+        visit_free(v);
     }
 
     static void qmp_init_marshal(void)
diff --git a/hmp.c b/hmp.c
index 925601ac3e..0cf5baa069 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1722,7 +1722,7 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
 {
     Error *err = NULL;
     QemuOpts *opts;
-    OptsVisitor *ov;
+    Visitor *v;
     Object *obj = NULL;
 
     opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
@@ -1731,9 +1731,9 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
         return;
     }
 
-    ov = opts_visitor_new(opts);
-    obj = user_creatable_add(qdict, opts_get_visitor(ov), &err);
-    opts_visitor_cleanup(ov);
+    v = opts_visitor_new(opts);
+    obj = user_creatable_add(qdict, v, &err);
+    visit_free(v);
     qemu_opts_del(opts);
 
     if (err) {
@@ -1983,15 +1983,14 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict)
     Error *err = NULL;
     MemdevList *memdev_list = qmp_query_memdev(&err);
     MemdevList *m = memdev_list;
-    StringOutputVisitor *ov;
+    Visitor *v;
     char *str;
     int i = 0;
 
 
     while (m) {
-        ov = string_output_visitor_new(false);
-        visit_type_uint16List(string_output_get_visitor(ov), NULL,
-                              &m->value->host_nodes, NULL);
+        v = string_output_visitor_new(false, &str);
+        visit_type_uint16List(v, NULL, &m->value->host_nodes, NULL);
         monitor_printf(mon, "memory backend: %d\n", i);
         monitor_printf(mon, "  size:  %" PRId64 "\n", m->value->size);
         monitor_printf(mon, "  merge: %s\n",
@@ -2002,11 +2001,11 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict)
                        m->value->prealloc ? "true" : "false");
         monitor_printf(mon, "  policy: %s\n",
                        HostMemPolicy_lookup[m->value->policy]);
-        str = string_output_get_string(ov);
+        visit_complete(v, &str);
         monitor_printf(mon, "  host nodes: %s\n", str);
 
         g_free(str);
-        string_output_visitor_cleanup(ov);
+        visit_free(v);
         m = m->next;
         i++;
     }
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index d24b9a98c8..e890a5d675 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -239,11 +239,11 @@ void acpi_table_add(const QemuOpts *opts, Error **errp)
     char unsigned *blob = NULL;
 
     {
-        OptsVisitor *ov;
+        Visitor *v;
 
-        ov = opts_visitor_new(opts);
-        visit_type_AcpiTableOptions(opts_get_visitor(ov), NULL, &hdrs, &err);
-        opts_visitor_cleanup(ov);
+        v = opts_visitor_new(opts);
+        visit_type_AcpiTableOptions(v, NULL, &hdrs, &err);
+        visit_free(v);
     }
 
     if (err) {
diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
index e2d4e68ba3..048ce6a424 100644
--- a/hw/pci/pcie_aer.c
+++ b/hw/pci/pcie_aer.c
@@ -21,6 +21,7 @@
 #include "qemu/osdep.h"
 #include "sysemu/sysemu.h"
 #include "qapi/qmp/types.h"
+#include "qapi/qmp/qjson.h"
 #include "monitor/monitor.h"
 #include "hw/pci/pci_bridge.h"
 #include "hw/pci/pcie.h"
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index d276db3a72..26a067951c 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -300,7 +300,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
             /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
             g_assert(fdt_depth > 0);
             visit_check_struct(v, &err);
-            visit_end_struct(v);
+            visit_end_struct(v, NULL);
             if (err) {
                 error_propagate(errp, err);
                 return;
@@ -323,7 +323,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
                     return;
                 }
             }
-            visit_end_list(v);
+            visit_end_list(v, NULL);
             break;
         }
         default:
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 557d3f9e0c..1a22e6d993 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -139,13 +139,13 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name,
     }
     visit_check_struct(v, &err);
 out_nested:
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
 
     if (!err) {
         visit_check_struct(v, &err);
     }
 out_end:
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
 out:
     error_propagate(errp, err);
 }
diff --git a/include/io/task.h b/include/io/task.h
index a993212ad9..df9499aa3a 100644
--- a/include/io/task.h
+++ b/include/io/task.h
@@ -159,7 +159,7 @@ typedef int (*QIOTaskWorker)(QIOTask *task,
  *      QIOTask *task;
  *      SocketAddress *addrCopy;
  *
- *      qapi_copy_SocketAddress(&addrCopy, addr);
+ *      addrCopy = QAPI_CLONE(SocketAddress, addr);
  *      task = qio_task_new(OBJECT(obj), func, opaque, notify);
  *
  *      qio_task_run_in_thread(task, myobject_listen_worker,
diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h
new file mode 100644
index 0000000000..b16177e1ee
--- /dev/null
+++ b/include/qapi/clone-visitor.h
@@ -0,0 +1,39 @@
+/*
+ * Clone Visitor
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QAPI_CLONE_VISITOR_H
+#define QAPI_CLONE_VISITOR_H
+
+#include "qemu/typedefs.h"
+#include "qapi/visitor.h"
+#include "qapi-visit.h"
+
+/*
+ * The clone visitor is for direct use only by the QAPI_CLONE() macro;
+ * it requires that the root visit occur on an object, list, or
+ * alternate, and is not usable directly on built-in QAPI types.
+ */
+typedef struct QapiCloneVisitor QapiCloneVisitor;
+
+void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *,
+                                                     void **, Error **));
+
+/*
+ * Deep-clone QAPI object @src of the given @type, and return the result.
+ *
+ * Not usable on QAPI scalars (integers, strings, enums), nor on a
+ * QAPI object that references the 'any' type.  Safe when @src is NULL.
+ */
+#define QAPI_CLONE(type, src)                                           \
+    ((type *)qapi_clone(src,                                            \
+                        (void (*)(Visitor *, const char *, void**,      \
+                                  Error **))visit_type_ ## type))
+
+#endif
diff --git a/include/qapi/dealloc-visitor.h b/include/qapi/dealloc-visitor.h
index 45b06b248c..b3e5c85fd8 100644
--- a/include/qapi/dealloc-visitor.h
+++ b/include/qapi/dealloc-visitor.h
@@ -23,9 +23,6 @@ typedef struct QapiDeallocVisitor QapiDeallocVisitor;
  * qapi_free_FOO() functions, and is the only visitor designed to work
  * correctly in the face of a partially-constructed QAPI tree.
  */
-QapiDeallocVisitor *qapi_dealloc_visitor_new(void);
-void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *d);
-
-Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v);
+Visitor *qapi_dealloc_visitor_new(void);
 
 #endif
diff --git a/include/qapi/opts-visitor.h b/include/qapi/opts-visitor.h
index ae1bf7cf51..6462c96c29 100644
--- a/include/qapi/opts-visitor.h
+++ b/include/qapi/opts-visitor.h
@@ -35,8 +35,6 @@ typedef struct OptsVisitor OptsVisitor;
  * QTypes.  It also requires a non-null list argument to
  * visit_start_list().
  */
-OptsVisitor *opts_visitor_new(const QemuOpts *opts);
-void opts_visitor_cleanup(OptsVisitor *nv);
-Visitor *opts_get_visitor(OptsVisitor *nv);
+Visitor *opts_visitor_new(const QemuOpts *opts);
 
 #endif
diff --git a/include/qapi/qmp-input-visitor.h b/include/qapi/qmp-input-visitor.h
index b0624d8683..f3ff5f3e98 100644
--- a/include/qapi/qmp-input-visitor.h
+++ b/include/qapi/qmp-input-visitor.h
@@ -25,10 +25,6 @@ typedef struct QmpInputVisitor QmpInputVisitor;
  * Set @strict to reject a parse that doesn't consume all keys of a
  * dictionary; otherwise excess input is ignored.
  */
-QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict);
-
-void qmp_input_visitor_cleanup(QmpInputVisitor *v);
-
-Visitor *qmp_input_get_visitor(QmpInputVisitor *v);
+Visitor *qmp_input_visitor_new(QObject *obj, bool strict);
 
 #endif
diff --git a/include/qapi/qmp-output-visitor.h b/include/qapi/qmp-output-visitor.h
index 22667706ab..040fdda142 100644
--- a/include/qapi/qmp-output-visitor.h
+++ b/include/qapi/qmp-output-visitor.h
@@ -19,10 +19,12 @@
 
 typedef struct QmpOutputVisitor QmpOutputVisitor;
 
-QmpOutputVisitor *qmp_output_visitor_new(void);
-void qmp_output_visitor_cleanup(QmpOutputVisitor *v);
-
-QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
-Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
+/*
+ * Create a new QMP output visitor.
+ *
+ * If everything else succeeds, pass @result to visit_complete() to
+ * collect the result of the visit.
+ */
+Visitor *qmp_output_visitor_new(QObject **result);
 
 #endif
diff --git a/include/qapi/qmp/types.h b/include/qapi/qmp/types.h
index 7782ec5a60..f21ecf48fe 100644
--- a/include/qapi/qmp/types.h
+++ b/include/qapi/qmp/types.h
@@ -20,6 +20,5 @@
 #include "qapi/qmp/qstring.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qjson.h"
 
 #endif /* QEMU_OBJECTS_H */
diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h
index 7b76c2b9e3..33551340e3 100644
--- a/include/qapi/string-input-visitor.h
+++ b/include/qapi/string-input-visitor.h
@@ -22,9 +22,6 @@ typedef struct StringInputVisitor StringInputVisitor;
  * QAPI structs, alternates, null, or arbitrary QTypes.  It also
  * requires a non-null list argument to visit_start_list().
  */
-StringInputVisitor *string_input_visitor_new(const char *str);
-void string_input_visitor_cleanup(StringInputVisitor *v);
-
-Visitor *string_input_get_visitor(StringInputVisitor *v);
+Visitor *string_input_visitor_new(const char *str);
 
 #endif
diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h
index e10522a35b..268dfe9986 100644
--- a/include/qapi/string-output-visitor.h
+++ b/include/qapi/string-output-visitor.h
@@ -18,14 +18,18 @@
 typedef struct StringOutputVisitor StringOutputVisitor;
 
 /*
+ * Create a new string output visitor.
+ *
+ * Using @human creates output that is a bit easier for humans to read
+ * (for example, showing integer values in both decimal and hex).
+ *
+ * If everything else succeeds, pass @result to visit_complete() to
+ * collect the result of the visit.
+ *
  * The string output visitor does not implement support for visiting
  * QAPI structs, alternates, null, or arbitrary QTypes.  It also
  * requires a non-null list argument to visit_start_list().
  */
-StringOutputVisitor *string_output_visitor_new(bool human);
-void string_output_visitor_cleanup(StringOutputVisitor *v);
-
-char *string_output_get_string(StringOutputVisitor *v);
-Visitor *string_output_get_visitor(StringOutputVisitor *v);
+Visitor *string_output_visitor_new(bool human, char **result);
 
 #endif
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 145afd03e7..8bd47ee4bb 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -27,14 +27,18 @@
  */
 
 /*
- * There are three classes of visitors; setting the class determines
+ * There are four classes of visitors; setting the class determines
  * how QAPI enums are visited, as well as what additional restrictions
- * can be asserted.
+ * can be asserted.  The values are intentionally chosen so as to
+ * permit some assertions based on whether a given bit is set (that
+ * is, some assertions apply to input and clone visitors, some
+ * assertions apply to output and clone visitors).
  */
 typedef enum VisitorType {
-    VISITOR_INPUT,
-    VISITOR_OUTPUT,
-    VISITOR_DEALLOC,
+    VISITOR_INPUT = 1,
+    VISITOR_OUTPUT = 2,
+    VISITOR_CLONE = 3,
+    VISITOR_DEALLOC = 4,
 } VisitorType;
 
 struct Visitor
@@ -47,7 +51,7 @@ struct Visitor
     void (*check_struct)(Visitor *v, Error **errp);
 
     /* Must be set to visit structs */
-    void (*end_struct)(Visitor *v);
+    void (*end_struct)(Visitor *v, void **obj);
 
     /* Must be set; implementations may require @list to be non-null,
      * but must document it. */
@@ -58,7 +62,7 @@ struct Visitor
     GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size);
 
     /* Must be set */
-    void (*end_list)(Visitor *v);
+    void (*end_list)(Visitor *v, void **list);
 
     /* Must be set by input and dealloc visitors to visit alternates;
      * optional for output visitors. */
@@ -67,7 +71,7 @@ struct Visitor
                             bool promote_int, Error **errp);
 
     /* Optional, needed for dealloc visitor */
-    void (*end_alternate)(Visitor *v);
+    void (*end_alternate)(Visitor *v, void **obj);
 
     /* Must be set */
     void (*type_int64)(Visitor *v, const char *name, int64_t *obj,
@@ -104,6 +108,12 @@ struct Visitor
 
     /* Must be set */
     VisitorType type;
+
+    /* Must be set for output visitors, optional otherwise. */
+    void (*complete)(Visitor *v, void *opaque);
+
+    /* Must be set */
+    void (*free)(Visitor *v);
 };
 
 #endif
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 4d12167bdc..fb8f4eb6ec 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -24,19 +24,32 @@
  * for doing work at each node of a QAPI graph; it can also be used
  * for a virtual walk, where there is no actual QAPI C struct.
  *
- * There are three kinds of visitor classes: input visitors (QMP,
+ * There are four kinds of visitor classes: input visitors (QMP,
  * string, and QemuOpts) parse an external representation and build
  * the corresponding QAPI graph, output visitors (QMP and string) take
- * a completed QAPI graph and generate an external representation, and
- * the dealloc visitor can take a QAPI graph (possibly partially
- * constructed) and recursively free its resources.  While the dealloc
- * and QMP input/output visitors are general, the string and QemuOpts
- * visitors have some implementation limitations; see the
- * documentation for each visitor for more details on what it
+ * a completed QAPI graph and generate an external representation, the
+ * dealloc visitor can take a QAPI graph (possibly partially
+ * constructed) and recursively free its resources, and the clone
+ * visitor performs a deep clone of one QAPI object to another.  While
+ * the dealloc and QMP input/output visitors are general, the string,
+ * QemuOpts, and clone visitors have some implementation limitations;
+ * see the documentation for each visitor for more details on what it
  * supports.  Also, see visitor-impl.h for the callback contracts
  * implemented by each visitor, and docs/qapi-code-gen.txt for more
  * about the QAPI code generator.
  *
+ * All of the visitors are created via:
+ *
+ * Visitor *subtype_visitor_new(parameters...);
+ *
+ * A visitor should be used for exactly one top-level visit_type_FOO()
+ * or virtual walk; if that is successful, the caller can optionally
+ * call visit_complete() (for now, useful only for output visits, but
+ * safe to call on all visits).  Then, regardless of success or
+ * failure, the user should call visit_free() to clean up resources.
+ * It is okay to free the visitor without completing the visit, if
+ * some other error is detected in the meantime.
+ *
  * All QAPI types have a corresponding function with a signature
  * roughly compatible with this:
  *
@@ -68,9 +81,9 @@
  *
  * If an error is detected during visit_type_FOO() with an input
  * visitor, then *@obj will be NULL for pointer types, and left
- * unchanged for scalar types.  Using an output visitor with an
- * incomplete object has undefined behavior (other than a special case
- * for visit_type_str() treating NULL like ""), while the dealloc
+ * unchanged for scalar types.  Using an output or clone visitor with
+ * an incomplete object has undefined behavior (other than a special
+ * case for visit_type_str() treating NULL like ""), while the dealloc
  * visitor safely handles incomplete objects.  Since input visitors
  * never produce an incomplete object, such an object is possible only
  * by manual construction.
@@ -90,11 +103,19 @@
  *
  * void qapi_free_FOO(FOO *obj);
  *
- * which behaves like free() in that @obj may be NULL.  Because of
- * these functions, the dealloc visitor is seldom used directly
- * outside of generated code.  QAPI types can also inherit from a base
- * class; when this happens, a function is generated for easily going
- * from the derived type to the base type:
+ * where behaves like free() in that @obj may be NULL.  Such objects
+ * may also be used with the following macro, provided alongside the
+ * clone visitor:
+ *
+ * Type *QAPI_CLONE(Type, src);
+ *
+ * in order to perform a deep clone of @src.  Because of the generated
+ * qapi_free functions and the QAPI_CLONE() macro, the clone and
+ * dealloc visitor should not be used directly outside of QAPI code.
+ *
+ * QAPI types can also inherit from a base class; when this happens, a
+ * function is generated for easily going from the derived type to the
+ * base type:
  *
  * BASE *qapi_CHILD_base(CHILD *obj);
  *
@@ -105,14 +126,14 @@
  *  Error *err = NULL;
  *  Visitor *v;
  *
- *  v = ...obtain input visitor...
+ *  v = FOO_visitor_new(...);
  *  visit_type_Foo(v, NULL, &f, &err);
  *  if (err) {
  *      ...handle error...
  *  } else {
  *      ...use f...
  *  }
- *  ...clean up v...
+ *  visit_free(v);
  *  qapi_free_Foo(f);
  * </example>
  *
@@ -122,7 +143,7 @@
  *  Error *err = NULL;
  *  Visitor *v;
  *
- *  v = ...obtain input visitor...
+ *  v = FOO_visitor_new(...);
  *  visit_type_FooList(v, NULL, &l, &err);
  *  if (err) {
  *      ...handle error...
@@ -131,7 +152,7 @@
  *          ...use l->value...
  *      }
  *  }
- *  ...clean up v...
+ *  visit_free(v);
  *  qapi_free_FooList(l);
  * </example>
  *
@@ -141,13 +162,17 @@
  *  Foo *f = ...obtain populated object...
  *  Error *err = NULL;
  *  Visitor *v;
+ *  Type *result;
  *
- *  v = ...obtain output visitor...
+ *  v = FOO_visitor_new(..., &result);
  *  visit_type_Foo(v, NULL, &f, &err);
  *  if (err) {
  *      ...handle error...
+ *  } else {
+ *      visit_complete(v, &result);
+ *      ...use result...
  *  }
- *  ...clean up v...
+ *  visit_free(v);
  * </example>
  *
  * When visiting a real QAPI struct, this file provides several
@@ -173,7 +198,7 @@
  *  Error *err = NULL;
  *  int value;
  *
- *  v = ...obtain visitor...
+ *  v = FOO_visitor_new(...);
  *  visit_start_struct(v, NULL, NULL, 0, &err);
  *  if (err) {
  *      goto out;
@@ -193,15 +218,15 @@
  *      goto outlist;
  *  }
  * outlist:
- *  visit_end_list(v);
+ *  visit_end_list(v, NULL);
  *  if (!err) {
  *      visit_check_struct(v, &err);
  *  }
  * outobj:
- *  visit_end_struct(v);
+ *  visit_end_struct(v, NULL);
  * out:
  *  error_propagate(errp, err);
- *  ...clean up v...
+ *  visit_free(v);
  * </example>
  */
 
@@ -222,6 +247,31 @@ typedef struct GenericAlternate {
     char padding[];
 } GenericAlternate;
 
+/*** Visitor cleanup ***/
+
+/*
+ * Complete the visit, collecting any output.
+ *
+ * May only be called only once after a successful top-level
+ * visit_type_FOO() or visit_end_ITEM(), and marks the end of the
+ * visit.  The @opaque pointer should match the output parameter
+ * passed to the subtype_visitor_new() used to create an output
+ * visitor, or NULL for any other visitor.  Needed for output
+ * visitors, but may also be called with other visitors.
+ */
+void visit_complete(Visitor *v, void *opaque);
+
+/*
+ * Free @v and any resources it has tied up.
+ *
+ * May be called whether or not the visit has been successfully
+ * completed, but should not be called until a top-level
+ * visit_type_FOO() or visit_start_ITEM() has been performed on the
+ * visitor.  Safe if @v is NULL.
+ */
+void visit_free(Visitor *v);
+
+
 /*** Visiting structures ***/
 
 /*
@@ -231,9 +281,9 @@ typedef struct GenericAlternate {
  * container; see the general description of @name above.
  *
  * @obj must be non-NULL for a real walk, in which case @size
- * determines how much memory an input visitor will allocate into
- * *@obj.  @obj may also be NULL for a virtual walk, in which case
- * @size is ignored.
+ * determines how much memory an input or clone visitor will allocate
+ * into *@obj.  @obj may also be NULL for a virtual walk, in which
+ * case @size is ignored.
  *
  * @errp obeys typical error usage, and reports failures such as a
  * member @name is not present, or present but not an object.  On
@@ -242,8 +292,8 @@ typedef struct GenericAlternate {
  * After visit_start_struct() succeeds, the caller may visit its
  * members one after the other, passing the member's name and address
  * within the struct.  Finally, visit_end_struct() needs to be called
- * to clean up, even if intermediate visits fail.  See the examples
- * above.
+ * with the same @obj to clean up, even if intermediate visits fail.
+ * See the examples above.
  *
  * FIXME Should this be named visit_start_object, since it is also
  * used for QAPI unions, and maps to JSON objects?
@@ -267,12 +317,14 @@ void visit_check_struct(Visitor *v, Error **errp);
 /*
  * Complete an object visit started earlier.
  *
+ * @obj must match what was passed to the paired visit_start_struct().
+ *
  * Must be called after any successful use of visit_start_struct(),
  * even if intermediate processing was skipped due to errors, to allow
  * the backend to release any resources.  Destroying the visitor early
- * behaves as if this was implicitly called.
+ * with visit_free() behaves as if this was implicitly called.
  */
-void visit_end_struct(Visitor *v);
+void visit_end_struct(Visitor *v, void **obj);
 
 
 /*** Visiting lists ***/
@@ -284,9 +336,9 @@ void visit_end_struct(Visitor *v);
  * container; see the general description of @name above.
  *
  * @list must be non-NULL for a real walk, in which case @size
- * determines how much memory an input visitor will allocate into
- * *@list (at least sizeof(GenericList)).  Some visitors also allow
- * @list to be NULL for a virtual walk, in which case @size is
+ * determines how much memory an input or clone visitor will allocate
+ * into *@list (at least sizeof(GenericList)).  Some visitors also
+ * allow @list to be NULL for a virtual walk, in which case @size is
  * ignored.
  *
  * @errp obeys typical error usage, and reports failures such as a
@@ -299,8 +351,9 @@ void visit_end_struct(Visitor *v);
  * visit (where @obj is NULL) uses other means.  For each list
  * element, call the appropriate visit_type_FOO() with name set to
  * NULL and obj set to the address of the value member of the list
- * element.  Finally, visit_end_list() needs to be called to clean up,
- * even if intermediate visits fail.  See the examples above.
+ * element.  Finally, visit_end_list() needs to be called with the
+ * same @list to clean up, even if intermediate visits fail.  See the
+ * examples above.
  */
 void visit_start_list(Visitor *v, const char *name, GenericList **list,
                       size_t size, Error **errp);
@@ -324,12 +377,14 @@ GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size);
 /*
  * Complete a list visit started earlier.
  *
+ * @list must match what was passed to the paired visit_start_list().
+ *
  * Must be called after any successful use of visit_start_list(), even
  * if intermediate processing was skipped due to errors, to allow the
  * backend to release any resources.  Destroying the visitor early
- * behaves as if this was implicitly called.
+ * with visit_free() behaves as if this was implicitly called.
  */
-void visit_end_list(Visitor *v);
+void visit_end_list(Visitor *v, void **list);
 
 
 /*** Visiting alternates ***/
@@ -340,15 +395,16 @@ void visit_end_list(Visitor *v);
  * @name expresses the relationship of this alternate to its parent
  * container; see the general description of @name above.
  *
- * @obj must not be NULL. Input visitors use @size to determine how
- * much memory to allocate into *@obj, then determine the qtype of the
- * next thing to be visited, stored in (*@obj)->type.  Other visitors
- * will leave @obj unchanged.
+ * @obj must not be NULL. Input and clone visitors use @size to
+ * determine how much memory to allocate into *@obj, then determine
+ * the qtype of the next thing to be visited, stored in (*@obj)->type.
+ * Other visitors will leave @obj unchanged.
  *
  * If @promote_int, treat integers as QTYPE_FLOAT.
  *
- * If successful, this must be paired with visit_end_alternate() to
- * clean up, even if visiting the contents of the alternate fails.
+ * If successful, this must be paired with visit_end_alternate() with
+ * the same @obj to clean up, even if visiting the contents of the
+ * alternate fails.
  */
 void visit_start_alternate(Visitor *v, const char *name,
                            GenericAlternate **obj, size_t size,
@@ -357,15 +413,15 @@ void visit_start_alternate(Visitor *v, const char *name,
 /*
  * Finish visiting an alternate type.
  *
+ * @obj must match what was passed to the paired visit_start_alternate().
+ *
  * Must be called after any successful use of visit_start_alternate(),
  * even if intermediate processing was skipped due to errors, to allow
  * the backend to release any resources.  Destroying the visitor early
- * behaves as if this was implicitly called.
+ * with visit_free() behaves as if this was implicitly called.
  *
- * TODO: Should all the visit_end_* interfaces take obj parameter, so
- * that dealloc visitor need not track what was passed in visit_start?
  */
-void visit_end_alternate(Visitor *v);
+void visit_end_alternate(Visitor *v, void **obj);
 
 
 /*** Other helpers ***/
@@ -507,9 +563,10 @@ void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp);
  * @name expresses the relationship of this string to its parent
  * container; see the general description of @name above.
  *
- * @obj must be non-NULL.  Input visitors set *@obj to the value
- * (never NULL).  Other visitors leave *@obj unchanged, and commonly
- * treat NULL like "".
+ * @obj must be non-NULL.  Input and clone visitors set *@obj to the
+ * value (always using "" rather than NULL for an empty string).
+ * Other visitors leave *@obj unchanged, and commonly treat NULL like
+ * "".
  *
  * It is safe to cast away const when preparing a (const char *) value
  * into @obj for use by an output visitor.
diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 462033a4de..2f3763f781 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -107,10 +107,6 @@ SocketAddress *socket_local_address(int fd, Error **errp);
  */
 SocketAddress *socket_remote_address(int fd, Error **errp);
 
-
-void qapi_copy_SocketAddress(SocketAddress **p_dest,
-                             SocketAddress *src);
-
 /**
  * socket_address_to_string:
  * @addr: the socket address struct
diff --git a/io/channel-socket.c b/io/channel-socket.c
index 6ec87f8cdb..196a4f18f7 100644
--- a/io/channel-socket.c
+++ b/io/channel-socket.c
@@ -23,6 +23,7 @@
 #include "io/channel-socket.h"
 #include "io/channel-watch.h"
 #include "trace.h"
+#include "qapi/clone-visitor.h"
 
 #define SOCKET_MAX_FDS 16
 
@@ -189,7 +190,7 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc,
         OBJECT(ioc), callback, opaque, destroy);
     SocketAddress *addrCopy;
 
-    qapi_copy_SocketAddress(&addrCopy, addr);
+    addrCopy = QAPI_CLONE(SocketAddress, addr);
 
     /* socket_connect() does a non-blocking connect(), but it
      * still blocks in DNS lookups, so we must use a thread */
@@ -251,7 +252,7 @@ void qio_channel_socket_listen_async(QIOChannelSocket *ioc,
         OBJECT(ioc), callback, opaque, destroy);
     SocketAddress *addrCopy;
 
-    qapi_copy_SocketAddress(&addrCopy, addr);
+    addrCopy = QAPI_CLONE(SocketAddress, addr);
 
     /* socket_listen() blocks in DNS lookups, so we must use a thread */
     trace_qio_channel_socket_listen_async(ioc, addr);
@@ -331,8 +332,8 @@ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc,
     struct QIOChannelSocketDGramWorkerData *data = g_new0(
         struct QIOChannelSocketDGramWorkerData, 1);
 
-    qapi_copy_SocketAddress(&data->localAddr, localAddr);
-    qapi_copy_SocketAddress(&data->remoteAddr, remoteAddr);
+    data->localAddr = QAPI_CLONE(SocketAddress, localAddr);
+    data->remoteAddr = QAPI_CLONE(SocketAddress, remoteAddr);
 
     trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr);
     qio_task_run_in_thread(task,
diff --git a/monitor.c b/monitor.c
index 6f960f13a6..ed09cdc261 100644
--- a/monitor.c
+++ b/monitor.c
@@ -54,11 +54,7 @@
 #include "qemu/acl.h"
 #include "sysemu/tpm.h"
 #include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qfloat.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/types.h"
 #include "qapi/qmp/qjson.h"
 #include "qapi/qmp/json-streamer.h"
 #include "qapi/qmp/json-parser.h"
diff --git a/net/net.c b/net/net.c
index 75bb1770f9..019aaad0fc 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1024,8 +1024,7 @@ int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
     void *object = NULL;
     Error *err = NULL;
     int ret = -1;
-    OptsVisitor *ov = opts_visitor_new(opts);
-    Visitor *v = opts_get_visitor(ov);
+    Visitor *v = opts_visitor_new(opts);
 
     {
         /* Parse convenience option format ip6-net=fec0::0[/64] */
@@ -1075,7 +1074,7 @@ int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
     }
 
     error_propagate(errp, err);
-    opts_visitor_cleanup(ov);
+    visit_free(v);
     return ret;
 }
 
@@ -1199,7 +1198,7 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
     char *str;
     ObjectProperty *prop;
     ObjectPropertyIterator iter;
-    StringOutputVisitor *ov;
+    Visitor *v;
 
     /* generate info str */
     object_property_iter_init(&iter, OBJECT(nf));
@@ -1207,11 +1206,10 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
         if (!strcmp(prop->name, "type")) {
             continue;
         }
-        ov = string_output_visitor_new(false);
-        object_property_get(OBJECT(nf), string_output_get_visitor(ov),
-                            prop->name, NULL);
-        str = string_output_get_string(ov);
-        string_output_visitor_cleanup(ov);
+        v = string_output_visitor_new(false, &str);
+        object_property_get(OBJECT(nf), v, prop->name, NULL);
+        visit_complete(v, &str);
+        visit_free(v);
         monitor_printf(mon, ",%s=%s", prop->name, str);
         g_free(str);
     }
diff --git a/numa.c b/numa.c
index 572712ccf9..cbae430e36 100644
--- a/numa.c
+++ b/numa.c
@@ -217,9 +217,9 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
     Error *err = NULL;
 
     {
-        OptsVisitor *ov = opts_visitor_new(opts);
-        visit_type_NumaOptions(opts_get_visitor(ov), NULL, &object, &err);
-        opts_visitor_cleanup(ov);
+        Visitor *v = opts_visitor_new(opts);
+        visit_type_NumaOptions(v, NULL, &object, &err);
+        visit_free(v);
     }
 
     if (err) {
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 2278970690..7ea4aebb00 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -1,6 +1,6 @@
 util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
 util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
 util-obj-y += string-input-visitor.o string-output-visitor.o
-util-obj-y += opts-visitor.o
+util-obj-y += opts-visitor.o qapi-clone-visitor.o
 util-obj-y += qmp-event.o
 util-obj-y += qapi-util.o
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 4cf1cf885b..1048bbc84e 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -180,7 +180,7 @@ opts_check_struct(Visitor *v, Error **errp)
 
 
 static void
-opts_end_struct(Visitor *v)
+opts_end_struct(Visitor *v, void **obj)
 {
     OptsVisitor *ov = to_ov(v);
 
@@ -273,7 +273,7 @@ opts_next_list(Visitor *v, GenericList *tail, size_t size)
 
 
 static void
-opts_end_list(Visitor *v)
+opts_end_list(Visitor *v, void **obj)
 {
     OptsVisitor *ov = to_ov(v);
 
@@ -513,7 +513,20 @@ opts_optional(Visitor *v, const char *name, bool *present)
 }
 
 
-OptsVisitor *
+static void
+opts_free(Visitor *v)
+{
+    OptsVisitor *ov = to_ov(v);
+
+    if (ov->unprocessed_opts != NULL) {
+        g_hash_table_destroy(ov->unprocessed_opts);
+    }
+    g_free(ov->fake_id_opt);
+    g_free(ov);
+}
+
+
+Visitor *
 opts_visitor_new(const QemuOpts *opts)
 {
     OptsVisitor *ov;
@@ -540,26 +553,9 @@ opts_visitor_new(const QemuOpts *opts)
      * skip some mandatory methods... */
 
     ov->visitor.optional = &opts_optional;
+    ov->visitor.free = opts_free;
 
     ov->opts_root = opts;
 
-    return ov;
-}
-
-
-void
-opts_visitor_cleanup(OptsVisitor *ov)
-{
-    if (ov->unprocessed_opts != NULL) {
-        g_hash_table_destroy(ov->unprocessed_opts);
-    }
-    g_free(ov->fake_id_opt);
-    g_free(ov);
-}
-
-
-Visitor *
-opts_get_visitor(OptsVisitor *ov)
-{
     return &ov->visitor;
 }
diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c
new file mode 100644
index 0000000000..0bb8216372
--- /dev/null
+++ b/qapi/qapi-clone-visitor.c
@@ -0,0 +1,182 @@
+/*
+ * Copy one QAPI object to another
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/clone-visitor.h"
+#include "qapi/visitor-impl.h"
+#include "qapi/error.h"
+
+struct QapiCloneVisitor {
+    Visitor visitor;
+    size_t depth;
+};
+
+static QapiCloneVisitor *to_qcv(Visitor *v)
+{
+    return container_of(v, QapiCloneVisitor, visitor);
+}
+
+static void qapi_clone_start_struct(Visitor *v, const char *name, void **obj,
+                                    size_t size, Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    if (!obj) {
+        assert(qcv->depth);
+        /* Only possible when visiting an alternate's object
+         * branch. Nothing further to do here, since the earlier
+         * visit_start_alternate() already copied memory. */
+        return;
+    }
+
+    *obj = g_memdup(*obj, size);
+    qcv->depth++;
+}
+
+static void qapi_clone_end(Visitor *v, void **obj)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    if (obj) {
+        qcv->depth--;
+    }
+}
+
+static void qapi_clone_start_list(Visitor *v, const char *name,
+                                  GenericList **listp, size_t size,
+                                  Error **errp)
+{
+    qapi_clone_start_struct(v, name, (void **)listp, size, errp);
+}
+
+static GenericList *qapi_clone_next_list(Visitor *v, GenericList *tail,
+                                         size_t size)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Unshare the tail of the list cloned by g_memdup() */
+    tail->next = g_memdup(tail->next, size);
+    return tail->next;
+}
+
+static void qapi_clone_start_alternate(Visitor *v, const char *name,
+                                       GenericAlternate **obj, size_t size,
+                                       bool promote_int, Error **errp)
+{
+    qapi_clone_start_struct(v, name, (void **)obj, size, errp);
+}
+
+static void qapi_clone_type_int64(Visitor *v, const char *name, int64_t *obj,
+                                   Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Value was already cloned by g_memdup() */
+}
+
+static void qapi_clone_type_uint64(Visitor *v, const char *name,
+                                    uint64_t *obj, Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Value was already cloned by g_memdup() */
+}
+
+static void qapi_clone_type_bool(Visitor *v, const char *name, bool *obj,
+                                  Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Value was already cloned by g_memdup() */
+}
+
+static void qapi_clone_type_str(Visitor *v, const char *name, char **obj,
+                                 Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /*
+     * Pointer was already cloned by g_memdup; create fresh copy.
+     * Note that as long as qmp-output-visitor accepts NULL instead of
+     * "", then we must do likewise. However, we want to obey the
+     * input visitor semantics of never producing NULL when the empty
+     * string is intended.
+     */
+    *obj = g_strdup(*obj ?: "");
+}
+
+static void qapi_clone_type_number(Visitor *v, const char *name, double *obj,
+                                    Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Value was already cloned by g_memdup() */
+}
+
+static void qapi_clone_type_null(Visitor *v, const char *name, Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Nothing to do */
+}
+
+static void qapi_clone_free(Visitor *v)
+{
+    g_free(v);
+}
+
+static Visitor *qapi_clone_visitor_new(void)
+{
+    QapiCloneVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->visitor.type = VISITOR_CLONE;
+    v->visitor.start_struct = qapi_clone_start_struct;
+    v->visitor.end_struct = qapi_clone_end;
+    v->visitor.start_list = qapi_clone_start_list;
+    v->visitor.next_list = qapi_clone_next_list;
+    v->visitor.end_list = qapi_clone_end;
+    v->visitor.start_alternate = qapi_clone_start_alternate;
+    v->visitor.end_alternate = qapi_clone_end;
+    v->visitor.type_int64 = qapi_clone_type_int64;
+    v->visitor.type_uint64 = qapi_clone_type_uint64;
+    v->visitor.type_bool = qapi_clone_type_bool;
+    v->visitor.type_str = qapi_clone_type_str;
+    v->visitor.type_number = qapi_clone_type_number;
+    v->visitor.type_null = qapi_clone_type_null;
+    v->visitor.free = qapi_clone_free;
+
+    return &v->visitor;
+}
+
+void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *,
+                                                     void **, Error **))
+{
+    Visitor *v;
+    void *dst = (void *) src; /* Cast away const */
+
+    if (!src) {
+        return NULL;
+    }
+
+    v = qapi_clone_visitor_new();
+    visit_type(v, NULL, &dst, &error_abort);
+    visit_free(v);
+    return dst;
+}
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index cd68b55a1a..e39457bc79 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -19,53 +19,18 @@
 #include "qapi/qmp/types.h"
 #include "qapi/visitor-impl.h"
 
-typedef struct StackEntry
-{
-    void *value;
-    QTAILQ_ENTRY(StackEntry) node;
-} StackEntry;
-
 struct QapiDeallocVisitor
 {
     Visitor visitor;
-    QTAILQ_HEAD(, StackEntry) stack;
 };
 
-static QapiDeallocVisitor *to_qov(Visitor *v)
-{
-    return container_of(v, QapiDeallocVisitor, visitor);
-}
-
-static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value)
-{
-    StackEntry *e = g_malloc0(sizeof(*e));
-
-    e->value = value;
-
-    QTAILQ_INSERT_HEAD(&qov->stack, e, node);
-}
-
-static void *qapi_dealloc_pop(QapiDeallocVisitor *qov)
-{
-    StackEntry *e = QTAILQ_FIRST(&qov->stack);
-    QObject *value;
-    QTAILQ_REMOVE(&qov->stack, e, node);
-    value = e->value;
-    g_free(e);
-    return value;
-}
-
 static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
                                       size_t unused, Error **errp)
 {
-    QapiDeallocVisitor *qov = to_qov(v);
-    qapi_dealloc_push(qov, obj);
 }
 
-static void qapi_dealloc_end_struct(Visitor *v)
+static void qapi_dealloc_end_struct(Visitor *v, void **obj)
 {
-    QapiDeallocVisitor *qov = to_qov(v);
-    void **obj = qapi_dealloc_pop(qov);
     if (obj) {
         g_free(*obj);
     }
@@ -75,14 +40,10 @@ static void qapi_dealloc_start_alternate(Visitor *v, const char *name,
                                          GenericAlternate **obj, size_t size,
                                          bool promote_int, Error **errp)
 {
-    QapiDeallocVisitor *qov = to_qov(v);
-    qapi_dealloc_push(qov, obj);
 }
 
-static void qapi_dealloc_end_alternate(Visitor *v)
+static void qapi_dealloc_end_alternate(Visitor *v, void **obj)
 {
-    QapiDeallocVisitor *qov = to_qov(v);
-    void **obj = qapi_dealloc_pop(qov);
     if (obj) {
         g_free(*obj);
     }
@@ -102,7 +63,7 @@ static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *tail,
     return next;
 }
 
-static void qapi_dealloc_end_list(Visitor *v)
+static void qapi_dealloc_end_list(Visitor *v, void **obj)
 {
 }
 
@@ -146,17 +107,12 @@ static void qapi_dealloc_type_null(Visitor *v, const char *name, Error **errp)
 {
 }
 
-Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
+static void qapi_dealloc_free(Visitor *v)
 {
-    return &v->visitor;
+    g_free(container_of(v, QapiDeallocVisitor, visitor));
 }
 
-void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v)
-{
-    g_free(v);
-}
-
-QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
+Visitor *qapi_dealloc_visitor_new(void)
 {
     QapiDeallocVisitor *v;
 
@@ -177,8 +133,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.type_number = qapi_dealloc_type_number;
     v->visitor.type_any = qapi_dealloc_type_anything;
     v->visitor.type_null = qapi_dealloc_type_null;
+    v->visitor.free = qapi_dealloc_free;
 
-    QTAILQ_INIT(&v->stack);
-
-    return v;
+    return &v->visitor;
 }
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index eada4676a2..55f5876dc0 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -20,6 +20,21 @@
 #include "qapi/visitor.h"
 #include "qapi/visitor-impl.h"
 
+void visit_complete(Visitor *v, void *opaque)
+{
+    assert(v->type != VISITOR_OUTPUT || v->complete);
+    if (v->complete) {
+        v->complete(v, opaque);
+    }
+}
+
+void visit_free(Visitor *v)
+{
+    if (v) {
+        v->free(v);
+    }
+}
+
 void visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp)
 {
@@ -27,10 +42,10 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
 
     if (obj) {
         assert(size);
-        assert(v->type != VISITOR_OUTPUT || *obj);
+        assert(!(v->type & VISITOR_OUTPUT) || *obj);
     }
     v->start_struct(v, name, obj, size, &err);
-    if (obj && v->type == VISITOR_INPUT) {
+    if (obj && (v->type & VISITOR_INPUT)) {
         assert(!err != !*obj);
     }
     error_propagate(errp, err);
@@ -43,9 +58,9 @@ void visit_check_struct(Visitor *v, Error **errp)
     }
 }
 
-void visit_end_struct(Visitor *v)
+void visit_end_struct(Visitor *v, void **obj)
 {
-    v->end_struct(v);
+    v->end_struct(v, obj);
 }
 
 void visit_start_list(Visitor *v, const char *name, GenericList **list,
@@ -55,7 +70,7 @@ void visit_start_list(Visitor *v, const char *name, GenericList **list,
 
     assert(!list || size >= sizeof(GenericList));
     v->start_list(v, name, list, size, &err);
-    if (list && v->type == VISITOR_INPUT) {
+    if (list && (v->type & VISITOR_INPUT)) {
         assert(!(err && *list));
     }
     error_propagate(errp, err);
@@ -67,9 +82,9 @@ GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size)
     return v->next_list(v, tail, size);
 }
 
-void visit_end_list(Visitor *v)
+void visit_end_list(Visitor *v, void **obj)
 {
-    v->end_list(v);
+    v->end_list(v, obj);
 }
 
 void visit_start_alternate(Visitor *v, const char *name,
@@ -79,20 +94,20 @@ void visit_start_alternate(Visitor *v, const char *name,
     Error *err = NULL;
 
     assert(obj && size >= sizeof(GenericAlternate));
-    assert(v->type != VISITOR_OUTPUT || *obj);
+    assert(!(v->type & VISITOR_OUTPUT) || *obj);
     if (v->start_alternate) {
         v->start_alternate(v, name, obj, size, promote_int, &err);
     }
-    if (v->type == VISITOR_INPUT) {
+    if (v->type & VISITOR_INPUT) {
         assert(v->start_alternate && !err != !*obj);
     }
     error_propagate(errp, err);
 }
 
-void visit_end_alternate(Visitor *v)
+void visit_end_alternate(Visitor *v, void **obj)
 {
     if (v->end_alternate) {
-        v->end_alternate(v);
+        v->end_alternate(v, obj);
     }
 }
 
@@ -235,10 +250,10 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
     assert(obj);
     /* TODO: Fix callers to not pass NULL when they mean "", so that we
      * can enable:
-    assert(v->type != VISITOR_OUTPUT || *obj);
+    assert(!(v->type & VISITOR_OUTPUT) || *obj);
      */
     v->type_str(v, name, obj, &err);
-    if (v->type == VISITOR_INPUT) {
+    if (v->type & VISITOR_INPUT) {
         assert(!err != !*obj);
     }
     error_propagate(errp, err);
@@ -320,9 +335,19 @@ void visit_type_enum(Visitor *v, const char *name, int *obj,
                      const char *const strings[], Error **errp)
 {
     assert(obj && strings);
-    if (v->type == VISITOR_INPUT) {
+    switch (v->type) {
+    case VISITOR_INPUT:
         input_type_enum(v, name, obj, strings, errp);
-    } else if (v->type == VISITOR_OUTPUT) {
+        break;
+    case VISITOR_OUTPUT:
         output_type_enum(v, name, obj, strings, errp);
+        break;
+    case VISITOR_CLONE:
+        /* nothing further to do, scalar value was already copied by
+         * g_memdup() during visit_start_*() */
+        break;
+    case VISITOR_DEALLOC:
+        /* nothing to deallocate for a scalar */
+        break;
     }
 }
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index 08faf853ac..505eb418ac 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -16,6 +16,7 @@
 #include "qapi/qmp/types.h"
 #include "qapi/qmp/dispatch.h"
 #include "qapi/qmp/json-parser.h"
+#include "qapi/qmp/qjson.h"
 #include "qapi-types.h"
 #include "qapi/qmp/qerror.h"
 
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index aea90a1378..21edb3928e 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -26,6 +26,7 @@
 typedef struct StackObject
 {
     QObject *obj; /* Object being visited */
+    void *qapi; /* sanity check that caller uses same pointer */
 
     GHashTable *h;           /* If obj is dict: unvisited keys */
     const QListEntry *entry; /* If obj is list: unvisited tail */
@@ -96,7 +97,7 @@ static void qdict_add_key(const char *key, QObject *obj, void *opaque)
 }
 
 static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
-                                        Error **errp)
+                                        void *qapi, Error **errp)
 {
     GHashTable *h;
     StackObject *tos = &qiv->stack[qiv->nb_stack];
@@ -108,6 +109,7 @@ static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
     }
 
     tos->obj = obj;
+    tos->qapi = qapi;
     assert(!tos->h);
     assert(!tos->entry);
 
@@ -145,12 +147,13 @@ static void qmp_input_check_struct(Visitor *v, Error **errp)
     }
 }
 
-static void qmp_input_pop(Visitor *v)
+static void qmp_input_pop(Visitor *v, void **obj)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
 
     assert(qiv->nb_stack > 0);
+    assert(tos->qapi == obj);
 
     if (qiv->strict) {
         GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
@@ -179,7 +182,7 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
         return;
     }
 
-    qmp_input_push(qiv, qobj, &err);
+    qmp_input_push(qiv, qobj, obj, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -207,7 +210,7 @@ static void qmp_input_start_list(Visitor *v, const char *name,
         return;
     }
 
-    entry = qmp_input_push(qiv, qobj, errp);
+    entry = qmp_input_push(qiv, qobj, list, errp);
     if (list) {
         if (entry) {
             *list = g_malloc0(size);
@@ -370,18 +373,15 @@ static void qmp_input_optional(Visitor *v, const char *name, bool *present)
     *present = true;
 }
 
-Visitor *qmp_input_get_visitor(QmpInputVisitor *v)
+static void qmp_input_free(Visitor *v)
 {
-    return &v->visitor;
+    QmpInputVisitor *qiv = to_qiv(v);
+
+    qobject_decref(qiv->root);
+    g_free(qiv);
 }
 
-void qmp_input_visitor_cleanup(QmpInputVisitor *v)
-{
-    qobject_decref(v->root);
-    g_free(v);
-}
-
-QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict)
+Visitor *qmp_input_visitor_new(QObject *obj, bool strict)
 {
     QmpInputVisitor *v;
 
@@ -403,10 +403,11 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict)
     v->visitor.type_any = qmp_input_type_any;
     v->visitor.type_null = qmp_input_type_null;
     v->visitor.optional = qmp_input_optional;
+    v->visitor.free = qmp_input_free;
     v->strict = strict;
 
     v->root = obj;
     qobject_incref(obj);
 
-    return v;
+    return &v->visitor;
 }
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 4d3cf78333..0452056d42 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -22,6 +22,7 @@
 typedef struct QStackEntry
 {
     QObject *value;
+    void *qapi; /* sanity check that caller uses same pointer */
     QTAILQ_ENTRY(QStackEntry) node;
 } QStackEntry;
 
@@ -32,11 +33,13 @@ struct QmpOutputVisitor
     Visitor visitor;
     QStack stack; /* Stack of containers that haven't yet been finished */
     QObject *root; /* Root of the output visit */
+    QObject **result; /* User's storage location for result */
 };
 
 #define qmp_output_add(qov, name, value) \
     qmp_output_add_obj(qov, name, QOBJECT(value))
-#define qmp_output_push(qov, value) qmp_output_push_obj(qov, QOBJECT(value))
+#define qmp_output_push(qov, value, qapi) \
+    qmp_output_push_obj(qov, QOBJECT(value), qapi)
 
 static QmpOutputVisitor *to_qov(Visitor *v)
 {
@@ -44,23 +47,26 @@ static QmpOutputVisitor *to_qov(Visitor *v)
 }
 
 /* Push @value onto the stack of current QObjects being built */
-static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
+static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value,
+                                void *qapi)
 {
     QStackEntry *e = g_malloc0(sizeof(*e));
 
     assert(qov->root);
     assert(value);
     e->value = value;
+    e->qapi = qapi;
     QTAILQ_INSERT_HEAD(&qov->stack, e, node);
 }
 
 /* Pop a value off the stack of QObjects being built, and return it. */
-static QObject *qmp_output_pop(QmpOutputVisitor *qov)
+static QObject *qmp_output_pop(QmpOutputVisitor *qov, void *qapi)
 {
     QStackEntry *e = QTAILQ_FIRST(&qov->stack);
     QObject *value;
 
     assert(e);
+    assert(e->qapi == qapi);
     QTAILQ_REMOVE(&qov->stack, e, node);
     value = e->value;
     assert(value);
@@ -104,13 +110,13 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
     QDict *dict = qdict_new();
 
     qmp_output_add(qov, name, dict);
-    qmp_output_push(qov, dict);
+    qmp_output_push(qov, dict, obj);
 }
 
-static void qmp_output_end_struct(Visitor *v)
+static void qmp_output_end_struct(Visitor *v, void **obj)
 {
     QmpOutputVisitor *qov = to_qov(v);
-    QObject *value = qmp_output_pop(qov);
+    QObject *value = qmp_output_pop(qov, obj);
     assert(qobject_type(value) == QTYPE_QDICT);
 }
 
@@ -122,7 +128,7 @@ static void qmp_output_start_list(Visitor *v, const char *name,
     QList *list = qlist_new();
 
     qmp_output_add(qov, name, list);
-    qmp_output_push(qov, list);
+    qmp_output_push(qov, list, listp);
 }
 
 static GenericList *qmp_output_next_list(Visitor *v, GenericList *tail,
@@ -131,10 +137,10 @@ static GenericList *qmp_output_next_list(Visitor *v, GenericList *tail,
     return tail->next;
 }
 
-static void qmp_output_end_list(Visitor *v)
+static void qmp_output_end_list(Visitor *v, void **obj)
 {
     QmpOutputVisitor *qov = to_qov(v);
-    QObject *value = qmp_output_pop(qov);
+    QObject *value = qmp_output_pop(qov, obj);
     assert(qobject_type(value) == QTYPE_QLIST);
 }
 
@@ -195,34 +201,34 @@ static void qmp_output_type_null(Visitor *v, const char *name, Error **errp)
 /* Finish building, and return the root object.
  * The root object is never null. The caller becomes the object's
  * owner, and should use qobject_decref() when done with it.  */
-QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
+static void qmp_output_complete(Visitor *v, void *opaque)
 {
+    QmpOutputVisitor *qov = to_qov(v);
+
     /* A visit must have occurred, with each start paired with end.  */
     assert(qov->root && QTAILQ_EMPTY(&qov->stack));
+    assert(opaque == qov->result);
 
     qobject_incref(qov->root);
-    return qov->root;
+    *qov->result = qov->root;
+    qov->result = NULL;
 }
 
-Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
-{
-    return &v->visitor;
-}
-
-void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
+static void qmp_output_free(Visitor *v)
 {
+    QmpOutputVisitor *qov = to_qov(v);
     QStackEntry *e, *tmp;
 
-    QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
-        QTAILQ_REMOVE(&v->stack, e, node);
+    QTAILQ_FOREACH_SAFE(e, &qov->stack, node, tmp) {
+        QTAILQ_REMOVE(&qov->stack, e, node);
         g_free(e);
     }
 
-    qobject_decref(v->root);
-    g_free(v);
+    qobject_decref(qov->root);
+    g_free(qov);
 }
 
-QmpOutputVisitor *qmp_output_visitor_new(void)
+Visitor *qmp_output_visitor_new(QObject **result)
 {
     QmpOutputVisitor *v;
 
@@ -241,8 +247,12 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
     v->visitor.type_number = qmp_output_type_number;
     v->visitor.type_any = qmp_output_type_any;
     v->visitor.type_null = qmp_output_type_null;
+    v->visitor.complete = qmp_output_complete;
+    v->visitor.free = qmp_output_free;
 
     QTAILQ_INIT(&v->stack);
+    *result = NULL;
+    v->result = result;
 
-    return v;
+    return &v->visitor;
 }
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 0690abb7de..8dfa561252 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -30,6 +30,7 @@ struct StringInputVisitor
     int64_t cur;
 
     const char *string;
+    void *list; /* Only needed for sanity checking the caller */
 };
 
 static StringInputVisitor *to_siv(Visitor *v)
@@ -120,6 +121,7 @@ start_list(Visitor *v, const char *name, GenericList **list, size_t size,
 
     /* We don't support visits without a list */
     assert(list);
+    siv->list = list;
 
     if (parse_str(siv, name, errp) < 0) {
         *list = NULL;
@@ -168,8 +170,11 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
     return tail->next;
 }
 
-static void end_list(Visitor *v)
+static void end_list(Visitor *v, void **obj)
 {
+    StringInputVisitor *siv = to_siv(v);
+
+    assert(siv->list == obj);
 }
 
 static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
@@ -321,19 +326,16 @@ static void parse_optional(Visitor *v, const char *name, bool *present)
     *present = true;
 }
 
-Visitor *string_input_get_visitor(StringInputVisitor *v)
+static void string_input_free(Visitor *v)
 {
-    return &v->visitor;
+    StringInputVisitor *siv = to_siv(v);
+
+    g_list_foreach(siv->ranges, free_range, NULL);
+    g_list_free(siv->ranges);
+    g_free(siv);
 }
 
-void string_input_visitor_cleanup(StringInputVisitor *v)
-{
-    g_list_foreach(v->ranges, free_range, NULL);
-    g_list_free(v->ranges);
-    g_free(v);
-}
-
-StringInputVisitor *string_input_visitor_new(const char *str)
+Visitor *string_input_visitor_new(const char *str)
 {
     StringInputVisitor *v;
 
@@ -350,7 +352,8 @@ StringInputVisitor *string_input_visitor_new(const char *str)
     v->visitor.next_list = next_list;
     v->visitor.end_list = end_list;
     v->visitor.optional = parse_optional;
+    v->visitor.free = string_input_free;
 
     v->string = str;
-    return v;
+    return &v->visitor;
 }
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index c6f01f9f9c..94ac8211d1 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -58,12 +58,14 @@ struct StringOutputVisitor
     Visitor visitor;
     bool human;
     GString *string;
+    char **result;
     ListMode list_mode;
     union {
         int64_t s;
         uint64_t u;
     } range_start, range_end;
     GList *ranges;
+    void *list; /* Only needed for sanity checking the caller */
 };
 
 static StringOutputVisitor *to_sov(Visitor *v)
@@ -274,6 +276,7 @@ start_list(Visitor *v, const char *name, GenericList **list, size_t size,
     assert(sov->list_mode == LM_NONE);
     /* We don't support visits without a list */
     assert(list);
+    sov->list = list;
     /* List handling is only needed if there are at least two elements */
     if (*list && (*list)->next) {
         sov->list_mode = LM_STARTED;
@@ -291,10 +294,11 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
     return ret;
 }
 
-static void end_list(Visitor *v)
+static void end_list(Visitor *v, void **obj)
 {
     StringOutputVisitor *sov = to_sov(v);
 
+    assert(sov->list == obj);
     assert(sov->list_mode == LM_STARTED ||
            sov->list_mode == LM_END ||
            sov->list_mode == LM_NONE ||
@@ -302,16 +306,13 @@ static void end_list(Visitor *v)
     sov->list_mode = LM_NONE;
 }
 
-char *string_output_get_string(StringOutputVisitor *sov)
+static void string_output_complete(Visitor *v, void *opaque)
 {
-    char *string = g_string_free(sov->string, false);
-    sov->string = NULL;
-    return string;
-}
+    StringOutputVisitor *sov = to_sov(v);
 
-Visitor *string_output_get_visitor(StringOutputVisitor *sov)
-{
-    return &sov->visitor;
+    assert(opaque == sov->result);
+    *sov->result = g_string_free(sov->string, false);
+    sov->string = NULL;
 }
 
 static void free_range(void *range, void *dummy)
@@ -319,8 +320,10 @@ static void free_range(void *range, void *dummy)
     g_free(range);
 }
 
-void string_output_visitor_cleanup(StringOutputVisitor *sov)
+static void string_output_free(Visitor *v)
 {
+    StringOutputVisitor *sov = to_sov(v);
+
     if (sov->string) {
         g_string_free(sov->string, true);
     }
@@ -330,7 +333,7 @@ void string_output_visitor_cleanup(StringOutputVisitor *sov)
     g_free(sov);
 }
 
-StringOutputVisitor *string_output_visitor_new(bool human)
+Visitor *string_output_visitor_new(bool human, char **result)
 {
     StringOutputVisitor *v;
 
@@ -338,6 +341,9 @@ StringOutputVisitor *string_output_visitor_new(bool human)
 
     v->string = g_string_new(NULL);
     v->human = human;
+    v->result = result;
+    *result = NULL;
+
     v->visitor.type = VISITOR_OUTPUT;
     v->visitor.type_int64 = print_type_int64;
     v->visitor.type_uint64 = print_type_uint64;
@@ -348,6 +354,8 @@ StringOutputVisitor *string_output_visitor_new(bool human)
     v->visitor.start_list = start_list;
     v->visitor.next_list = next_list;
     v->visitor.end_list = end_list;
+    v->visitor.complete = string_output_complete;
+    v->visitor.free = string_output_free;
 
-    return v;
+    return &v->visitor;
 }
diff --git a/qemu-char.c b/qemu-char.c
index b73969ddbd..0698b98750 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -32,8 +32,7 @@
 #include "sysemu/char.h"
 #include "hw/usb.h"
 #include "qmp-commands.h"
-#include "qapi/qmp-input-visitor.h"
-#include "qapi/qmp-output-visitor.h"
+#include "qapi/clone-visitor.h"
 #include "qapi-visit.h"
 #include "qemu/base64.h"
 #include "io/channel-socket.h"
@@ -4389,7 +4388,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
         }
     }
 
-    qapi_copy_SocketAddress(&s->addr, sock->addr);
+    s->addr = QAPI_CLONE(SocketAddress, sock->addr);
 
     chr->opaque = s;
     chr->chr_write = tcp_chr_write;
diff --git a/qemu-img.c b/qemu-img.c
index 2e04e281aa..ea5970bdd3 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -490,18 +490,17 @@ fail:
 
 static void dump_json_image_check(ImageCheck *check, bool quiet)
 {
-    Error *local_err = NULL;
     QString *str;
-    QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
-    visit_type_ImageCheck(qmp_output_get_visitor(ov), NULL, &check,
-                          &local_err);
-    obj = qmp_output_get_qobject(ov);
+    Visitor *v = qmp_output_visitor_new(&obj);
+
+    visit_type_ImageCheck(v, NULL, &check, &error_abort);
+    visit_complete(v, &obj);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
     qprintf(quiet, "%s\n", qstring_get_str(str));
     qobject_decref(obj);
-    qmp_output_visitor_cleanup(ov);
+    visit_free(v);
     QDECREF(str);
 }
 
@@ -2182,34 +2181,33 @@ static void dump_snapshots(BlockDriverState *bs)
 
 static void dump_json_image_info_list(ImageInfoList *list)
 {
-    Error *local_err = NULL;
     QString *str;
-    QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
-    visit_type_ImageInfoList(qmp_output_get_visitor(ov), NULL, &list,
-                             &local_err);
-    obj = qmp_output_get_qobject(ov);
+    Visitor *v = qmp_output_visitor_new(&obj);
+
+    visit_type_ImageInfoList(v, NULL, &list, &error_abort);
+    visit_complete(v, &obj);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
     printf("%s\n", qstring_get_str(str));
     qobject_decref(obj);
-    qmp_output_visitor_cleanup(ov);
+    visit_free(v);
     QDECREF(str);
 }
 
 static void dump_json_image_info(ImageInfo *info)
 {
-    Error *local_err = NULL;
     QString *str;
-    QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
-    visit_type_ImageInfo(qmp_output_get_visitor(ov), NULL, &info, &local_err);
-    obj = qmp_output_get_qobject(ov);
+    Visitor *v = qmp_output_visitor_new(&obj);
+
+    visit_type_ImageInfo(v, NULL, &info, &error_abort);
+    visit_complete(v, &obj);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
     printf("%s\n", qstring_get_str(str));
     qobject_decref(obj);
-    qmp_output_visitor_cleanup(ov);
+    visit_free(v);
     QDECREF(str);
 }
 
diff --git a/qmp.c b/qmp.c
index 7df6543871..b6d531ebe2 100644
--- a/qmp.c
+++ b/qmp.c
@@ -655,7 +655,7 @@ void qmp_object_add(const char *type, const char *id,
                     bool has_props, QObject *props, Error **errp)
 {
     const QDict *pdict = NULL;
-    QmpInputVisitor *qiv;
+    Visitor *v;
     Object *obj;
 
     if (props) {
@@ -666,10 +666,9 @@ void qmp_object_add(const char *type, const char *id,
         }
     }
 
-    qiv = qmp_input_visitor_new(props, true);
-    obj = user_creatable_add_type(type, id, pdict,
-                                  qmp_input_get_visitor(qiv), errp);
-    qmp_input_visitor_cleanup(qiv);
+    v = qmp_input_visitor_new(props, true);
+    obj = user_creatable_add_type(type, id, pdict, v, errp);
+    visit_free(v);
     if (obj) {
         object_unref(obj);
     }
diff --git a/qobject/json-parser.c b/qobject/json-parser.c
index 67ed727318..c18e48ab94 100644
--- a/qobject/json-parser.c
+++ b/qobject/json-parser.c
@@ -14,12 +14,7 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qemu-common.h"
-#include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qfloat.h"
-#include "qapi/qmp/qbool.h"
+#include "qapi/qmp/types.h"
 #include "qapi/qmp/json-parser.h"
 #include "qapi/qmp/json-lexer.h"
 #include "qapi/qmp/json-streamer.h"
diff --git a/qobject/qjson.c b/qobject/qjson.c
index ef160d2119..9a0de89079 100644
--- a/qobject/qjson.c
+++ b/qobject/qjson.c
@@ -16,11 +16,7 @@
 #include "qapi/qmp/json-parser.h"
 #include "qapi/qmp/json-streamer.h"
 #include "qapi/qmp/qjson.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qfloat.h"
-#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/types.h"
 #include "qemu/unicode.h"
 
 typedef struct JSONParsingState
diff --git a/qobject/qobject.c b/qobject/qobject.c
index cd41fb940b..fe4fa10989 100644
--- a/qobject/qobject.c
+++ b/qobject/qobject.c
@@ -9,12 +9,7 @@
 
 #include "qemu/osdep.h"
 #include "qemu-common.h"
-#include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qfloat.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/types.h"
 
 static void (*qdestroy[QTYPE__MAX])(QObject *) = {
     [QTYPE_NONE] = NULL,               /* No such object exists */
diff --git a/qom/object.c b/qom/object.c
index 9743ea4708..8166b7dace 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1221,8 +1221,7 @@ int object_property_get_enum(Object *obj, const char *name,
                              const char *typename, Error **errp)
 {
     Error *err = NULL;
-    StringOutputVisitor *sov;
-    StringInputVisitor *siv;
+    Visitor *v;
     char *str;
     int ret;
     ObjectProperty *prop = object_property_find(obj, name, errp);
@@ -1241,21 +1240,20 @@ int object_property_get_enum(Object *obj, const char *name,
 
     enumprop = prop->opaque;
 
-    sov = string_output_visitor_new(false);
-    object_property_get(obj, string_output_get_visitor(sov), name, &err);
+    v = string_output_visitor_new(false, &str);
+    object_property_get(obj, v, name, &err);
     if (err) {
         error_propagate(errp, err);
-        string_output_visitor_cleanup(sov);
+        visit_free(v);
         return 0;
     }
-    str = string_output_get_string(sov);
-    siv = string_input_visitor_new(str);
-    string_output_visitor_cleanup(sov);
-    visit_type_enum(string_input_get_visitor(siv), name, &ret,
-                    enumprop->strings, errp);
+    visit_complete(v, &str);
+    visit_free(v);
+    v = string_input_visitor_new(str);
+    visit_type_enum(v, name, &ret, enumprop->strings, errp);
 
     g_free(str);
-    string_input_visitor_cleanup(siv);
+    visit_free(v);
 
     return ret;
 }
@@ -1264,55 +1262,51 @@ void object_property_get_uint16List(Object *obj, const char *name,
                                     uint16List **list, Error **errp)
 {
     Error *err = NULL;
-    StringOutputVisitor *ov;
-    StringInputVisitor *iv;
+    Visitor *v;
     char *str;
 
-    ov = string_output_visitor_new(false);
-    object_property_get(obj, string_output_get_visitor(ov),
-                        name, &err);
+    v = string_output_visitor_new(false, &str);
+    object_property_get(obj, v, name, &err);
     if (err) {
         error_propagate(errp, err);
         goto out;
     }
-    str = string_output_get_string(ov);
-    iv = string_input_visitor_new(str);
-    visit_type_uint16List(string_input_get_visitor(iv), NULL, list, errp);
+    visit_complete(v, &str);
+    visit_free(v);
+    v = string_input_visitor_new(str);
+    visit_type_uint16List(v, NULL, list, errp);
 
     g_free(str);
-    string_input_visitor_cleanup(iv);
 out:
-    string_output_visitor_cleanup(ov);
+    visit_free(v);
 }
 
 void object_property_parse(Object *obj, const char *string,
                            const char *name, Error **errp)
 {
-    StringInputVisitor *siv;
-    siv = string_input_visitor_new(string);
-    object_property_set(obj, string_input_get_visitor(siv), name, errp);
-
-    string_input_visitor_cleanup(siv);
+    Visitor *v = string_input_visitor_new(string);
+    object_property_set(obj, v, name, errp);
+    visit_free(v);
 }
 
 char *object_property_print(Object *obj, const char *name, bool human,
                             Error **errp)
 {
-    StringOutputVisitor *sov;
+    Visitor *v;
     char *string = NULL;
     Error *local_err = NULL;
 
-    sov = string_output_visitor_new(human);
-    object_property_get(obj, string_output_get_visitor(sov), name, &local_err);
+    v = string_output_visitor_new(human, &string);
+    object_property_get(obj, v, name, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         goto out;
     }
 
-    string = string_output_get_string(sov);
+    visit_complete(v, &string);
 
 out:
-    string_output_visitor_cleanup(sov);
+    visit_free(v);
     return string;
 }
 
@@ -2044,7 +2038,7 @@ static void property_get_tm(Object *obj, Visitor *v, const char *name,
     }
     visit_check_struct(v, &err);
 out_end:
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
 out:
     error_propagate(errp, err);
 
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index 51e62e29d6..bf598468ab 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -71,7 +71,7 @@ Object *user_creatable_add(const QDict *qdict,
     obj = user_creatable_add_type(type, id, pdict, v, &local_err);
 
 out_visit:
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
 
 out:
     QDECREF(pdict);
@@ -127,7 +127,7 @@ Object *user_creatable_add_type(const char *type, const char *id,
     if (!local_err) {
         visit_check_struct(v, &local_err);
     }
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
     if (local_err) {
         goto out;
     }
@@ -156,15 +156,15 @@ out:
 
 Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
 {
-    OptsVisitor *ov;
+    Visitor *v;
     QDict *pdict;
     Object *obj = NULL;
 
-    ov = opts_visitor_new(opts);
+    v = opts_visitor_new(opts);
     pdict = qemu_opts_to_qdict(opts, NULL);
 
-    obj = user_creatable_add(pdict, opts_get_visitor(ov), errp);
-    opts_visitor_cleanup(ov);
+    obj = user_creatable_add(pdict, v, errp);
+    visit_free(v);
     QDECREF(pdict);
     return obj;
 }
diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c
index b66088d730..c225abcbad 100644
--- a/qom/qom-qobject.c
+++ b/qom/qom-qobject.c
@@ -21,12 +21,11 @@
 void object_property_set_qobject(Object *obj, QObject *value,
                                  const char *name, Error **errp)
 {
-    QmpInputVisitor *qiv;
+    Visitor *v;
     /* TODO: Should we reject, rather than ignore, excess input? */
-    qiv = qmp_input_visitor_new(value, false);
-    object_property_set(obj, qmp_input_get_visitor(qiv), name, errp);
-
-    qmp_input_visitor_cleanup(qiv);
+    v = qmp_input_visitor_new(value, false);
+    object_property_set(obj, v, name, errp);
+    visit_free(v);
 }
 
 QObject *object_property_get_qobject(Object *obj, const char *name,
@@ -34,14 +33,14 @@ QObject *object_property_get_qobject(Object *obj, const char *name,
 {
     QObject *ret = NULL;
     Error *local_err = NULL;
-    QmpOutputVisitor *qov;
+    Visitor *v;
 
-    qov = qmp_output_visitor_new();
-    object_property_get(obj, qmp_output_get_visitor(qov), name, &local_err);
+    v = qmp_output_visitor_new(&ret);
+    object_property_get(obj, v, name, &local_err);
     if (!local_err) {
-        ret = qmp_output_get_qobject(qov);
+        visit_complete(v, &ret);
     }
     error_propagate(errp, local_err);
-    qmp_output_visitor_cleanup(qov);
+    visit_free(v);
     return ret;
 }
diff --git a/replay/replay-input.c b/replay/replay-input.c
index 03e99d5aba..bd93554d8e 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -16,35 +16,7 @@
 #include "replay-internal.h"
 #include "qemu/notify.h"
 #include "ui/input.h"
-#include "qapi/qmp-output-visitor.h"
-#include "qapi/qmp-input-visitor.h"
-#include "qapi-visit.h"
-
-static InputEvent *qapi_clone_InputEvent(InputEvent *src)
-{
-    QmpOutputVisitor *qov;
-    QmpInputVisitor *qiv;
-    Visitor *ov, *iv;
-    QObject *obj;
-    InputEvent *dst = NULL;
-
-    qov = qmp_output_visitor_new();
-    ov = qmp_output_get_visitor(qov);
-    visit_type_InputEvent(ov, NULL, &src, &error_abort);
-    obj = qmp_output_get_qobject(qov);
-    qmp_output_visitor_cleanup(qov);
-    if (!obj) {
-        return NULL;
-    }
-
-    qiv = qmp_input_visitor_new(obj, true);
-    iv = qmp_input_get_visitor(qiv);
-    visit_type_InputEvent(iv, NULL, &dst, &error_abort);
-    qmp_input_visitor_cleanup(qiv);
-    qobject_decref(obj);
-
-    return dst;
-}
+#include "qapi/clone-visitor.h"
 
 void replay_save_input_event(InputEvent *evt)
 {
@@ -143,7 +115,7 @@ InputEvent *replay_read_input_event(void)
         break;
     }
 
-    return qapi_clone_InputEvent(&evt);
+    return QAPI_CLONE(InputEvent, &evt);
 }
 
 void replay_input_event(QemuConsole *src, InputEvent *evt)
@@ -151,7 +123,7 @@ void replay_input_event(QemuConsole *src, InputEvent *evt)
     if (replay_mode == REPLAY_MODE_PLAY) {
         /* Nothing */
     } else if (replay_mode == REPLAY_MODE_RECORD) {
-        replay_add_input_event(qapi_clone_InputEvent(evt));
+        replay_add_input_event(QAPI_CLONE(InputEvent, evt));
     } else {
         qemu_input_event_send_impl(src, evt);
     }
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 8c6acb3f3f..34b6a3a07f 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -61,24 +61,18 @@ def gen_marshal_output(ret_type):
 static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
 {
     Error *err = NULL;
-    QmpOutputVisitor *qov = qmp_output_visitor_new();
-    QapiDeallocVisitor *qdv;
     Visitor *v;
 
-    v = qmp_output_get_visitor(qov);
+    v = qmp_output_visitor_new(ret_out);
     visit_type_%(c_name)s(v, "unused", &ret_in, &err);
-    if (err) {
-        goto out;
+    if (!err) {
+        visit_complete(v, ret_out);
     }
-    *ret_out = qmp_output_get_qobject(qov);
-
-out:
     error_propagate(errp, err);
-    qmp_output_visitor_cleanup(qov);
-    qdv = qapi_dealloc_visitor_new();
-    v = qapi_dealloc_get_visitor(qdv);
+    visit_free(v);
+    v = qapi_dealloc_visitor_new();
     visit_type_%(c_name)s(v, "unused", &ret_in, NULL);
-    qapi_dealloc_visitor_cleanup(qdv);
+    visit_free(v);
 }
 ''',
                  c_type=ret_type.c_type(), c_name=ret_type.c_name())
@@ -115,12 +109,10 @@ def gen_marshal(name, arg_type, ret_type):
 
     if arg_type and arg_type.members:
         ret += mcgen('''
-    QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true);
-    QapiDeallocVisitor *qdv;
     Visitor *v;
     %(c_name)s arg = {0};
 
-    v = qmp_input_get_visitor(qiv);
+    v = qmp_input_visitor_new(QOBJECT(args), true);
     visit_start_struct(v, NULL, NULL, 0, &err);
     if (err) {
         goto out;
@@ -129,7 +121,7 @@ def gen_marshal(name, arg_type, ret_type):
     if (!err) {
         visit_check_struct(v, &err);
     }
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
     if (err) {
         goto out;
     }
@@ -155,13 +147,12 @@ def gen_marshal(name, arg_type, ret_type):
 ''')
     if arg_type and arg_type.members:
         ret += mcgen('''
-    qmp_input_visitor_cleanup(qiv);
-    qdv = qapi_dealloc_visitor_new();
-    v = qapi_dealloc_get_visitor(qdv);
+    visit_free(v);
+    v = qapi_dealloc_visitor_new();
     visit_start_struct(v, NULL, NULL, 0, NULL);
     visit_type_%(c_name)s_members(v, &arg, NULL);
-    visit_end_struct(v);
-    qapi_dealloc_visitor_cleanup(qdv);
+    visit_end_struct(v, NULL);
+    visit_free(v);
 ''',
                      c_name=arg_type.c_name())
 
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 21fb16744d..9c88627c9f 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -71,7 +71,7 @@ def gen_event_send(name, arg_type):
 
     if arg_type and arg_type.members:
         ret += mcgen('''
-    QmpOutputVisitor *qov;
+    QObject *obj;
     Visitor *v;
 ''')
         ret += gen_param_var(arg_type)
@@ -90,8 +90,7 @@ def gen_event_send(name, arg_type):
 
     if arg_type and arg_type.members:
         ret += mcgen('''
-    qov = qmp_output_visitor_new();
-    v = qmp_output_get_visitor(qov);
+    v = qmp_output_visitor_new(&obj);
 
     visit_start_struct(v, "%(name)s", NULL, 0, &err);
     if (err) {
@@ -101,12 +100,13 @@ def gen_event_send(name, arg_type):
     if (!err) {
         visit_check_struct(v, &err);
     }
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
     if (err) {
         goto out;
     }
 
-    qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov));
+    visit_complete(v, &obj);
+    qdict_put_obj(qmp, "data", obj);
 ''',
                      name=name, c_name=arg_type.c_name())
 
@@ -119,7 +119,7 @@ def gen_event_send(name, arg_type):
     if arg_type and arg_type.members:
         ret += mcgen('''
 out:
-    qmp_output_visitor_cleanup(qov);
+    visit_free(v);
 ''')
     ret += mcgen('''
     error_propagate(errp, err);
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 437cf6c8e3..5ace2cf065 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -150,17 +150,15 @@ def gen_type_cleanup(name):
 
 void qapi_free_%(c_name)s(%(c_name)s *obj)
 {
-    QapiDeallocVisitor *qdv;
     Visitor *v;
 
     if (!obj) {
         return;
     }
 
-    qdv = qapi_dealloc_visitor_new();
-    v = qapi_dealloc_get_visitor(qdv);
+    v = qapi_dealloc_visitor_new();
     visit_type_%(c_name)s(v, NULL, &obj, NULL);
-    qapi_dealloc_visitor_cleanup(qdv);
+    visit_free(v);
 }
 ''',
                 c_name=c_name(name))
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index ffb635c508..0b9e298bc4 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -129,7 +129,7 @@ def gen_visit_list(name, element_type):
         }
     }
 
-    visit_end_list(v);
+    visit_end_list(v, (void **)obj);
     if (err && visit_is_input(v)) {
         qapi_free_%(c_name)s(*obj);
         *obj = NULL;
@@ -194,7 +194,7 @@ def gen_visit_alternate(name, variants):
         if (!err) {
             visit_check_struct(v, &err);
         }
-        visit_end_struct(v);
+        visit_end_struct(v, NULL);
 ''',
                          c_type=var.type.c_name(),
                          c_name=c_name(var.name))
@@ -216,7 +216,7 @@ def gen_visit_alternate(name, variants):
                    "%(name)s");
     }
 out_obj:
-    visit_end_alternate(v);
+    visit_end_alternate(v, (void **)obj);
     if (err && visit_is_input(v)) {
         qapi_free_%(c_name)s(*obj);
         *obj = NULL;
@@ -250,7 +250,7 @@ def gen_visit_object(name, base, members, variants):
     }
     visit_check_struct(v, &err);
 out_obj:
-    visit_end_struct(v);
+    visit_end_struct(v, (void **)obj);
     if (err && visit_is_input(v)) {
         qapi_free_%(c_name)s(*obj);
         *obj = NULL;
diff --git a/tests/.gitignore b/tests/.gitignore
index 840ea396ab..dbb52639f6 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -13,6 +13,7 @@ test-aio
 test-base64
 test-bitops
 test-blockjob-txn
+test-clone-visitor
 test-coroutine
 test-crypto-afsplit
 test-crypto-block
diff --git a/tests/Makefile.include b/tests/Makefile.include
index f8e3c6b35a..2010b11b6c 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -22,6 +22,8 @@ check-unit-y += tests/check-qjson$(EXESUF)
 gcov-files-check-qjson-y = qobject/qjson.c
 check-unit-y += tests/test-qmp-output-visitor$(EXESUF)
 gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c
+check-unit-y += tests/test-clone-visitor$(EXESUF)
+gcov-files-test-clone-visitor-y = qapi/qapi-clone-visitor.c
 check-unit-y += tests/test-qmp-input-visitor$(EXESUF)
 gcov-files-test-qmp-input-visitor-y = qapi/qmp-input-visitor.c
 check-unit-y += tests/test-qmp-input-strict$(EXESUF)
@@ -402,6 +404,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
 	tests/check-qjson.o \
 	tests/test-coroutine.o tests/test-string-output-visitor.o \
 	tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \
+	tests/test-clone-visitor.o \
 	tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \
 	tests/test-qmp-commands.o tests/test-visitor-serialization.o \
 	tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \
@@ -499,6 +502,7 @@ tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(
 tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y)
 tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y)
 tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y)
+tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y)
 tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y)
 tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y)
 tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y)
diff --git a/tests/check-qjson.c b/tests/check-qjson.c
index 0e158f6eac..85955744eb 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qjson.c
@@ -12,14 +12,8 @@
  */
 #include "qemu/osdep.h"
 
-#include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qfloat.h"
-#include "qapi/qmp/qbool.h"
+#include "qapi/qmp/types.h"
 #include "qapi/qmp/qjson.h"
-
 #include "qemu-common.h"
 
 static void escaped_string(void)
diff --git a/tests/check-qnull.c b/tests/check-qnull.c
index 05d562d494..dc906b116e 100644
--- a/tests/check-qnull.c
+++ b/tests/check-qnull.c
@@ -37,8 +37,7 @@ static void qnull_ref_test(void)
 static void qnull_visit_test(void)
 {
     QObject *obj;
-    QmpOutputVisitor *qov;
-    QmpInputVisitor *qiv;
+    Visitor *v;
 
     /*
      * Most tests of interactions between QObject and visitors are in
@@ -48,17 +47,17 @@ static void qnull_visit_test(void)
 
     g_assert(qnull_.refcnt == 1);
     obj = qnull();
-    qiv = qmp_input_visitor_new(obj, true);
+    v = qmp_input_visitor_new(obj, true);
     qobject_decref(obj);
-    visit_type_null(qmp_input_get_visitor(qiv), NULL, &error_abort);
-    qmp_input_visitor_cleanup(qiv);
+    visit_type_null(v, NULL, &error_abort);
+    visit_free(v);
 
-    qov = qmp_output_visitor_new();
-    visit_type_null(qmp_output_get_visitor(qov), NULL, &error_abort);
-    obj = qmp_output_get_qobject(qov);
+    v = qmp_output_visitor_new(&obj);
+    visit_type_null(v, NULL, &error_abort);
+    visit_complete(v, &obj);
     g_assert(obj == &qnull_);
     qobject_decref(obj);
-    qmp_output_visitor_cleanup(qov);
+    visit_free(v);
 
     g_assert(qnull_.refcnt == 1);
 }
diff --git a/tests/test-clone-visitor.c b/tests/test-clone-visitor.c
new file mode 100644
index 0000000000..df0c045512
--- /dev/null
+++ b/tests/test-clone-visitor.c
@@ -0,0 +1,206 @@
+/*
+ * QAPI Clone Visitor unit-tests.
+ *
+ * Copyright (C) 2016 Red Hat Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include <glib.h>
+
+#include "qemu-common.h"
+#include "qapi/clone-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qapi/qmp/types.h"
+
+static void test_clone_struct(void)
+{
+    UserDefOne *src, *dst;
+
+    src = g_new0(UserDefOne, 1);
+    src->integer = 42;
+    src->string = g_strdup("Hello");
+    src->has_enum1 = false;
+    src->enum1 = ENUM_ONE_VALUE2;
+
+    dst = QAPI_CLONE(UserDefOne, src);
+    g_assert(dst);
+    g_assert_cmpint(dst->integer, ==, 42);
+    g_assert(dst->string != src->string);
+    g_assert_cmpstr(dst->string, ==, "Hello");
+    g_assert_cmpint(dst->has_enum1, ==, false);
+    /* Our implementation does this, but it is not required:
+    g_assert_cmpint(dst->enum1, ==, ENUM_ONE_VALUE2);
+    */
+
+    qapi_free_UserDefOne(src);
+    qapi_free_UserDefOne(dst);
+}
+
+static void test_clone_alternate(void)
+{
+    AltStrBool *b_src, *s_src, *b_dst, *s_dst;
+
+    b_src = g_new0(AltStrBool, 1);
+    b_src->type = QTYPE_QBOOL;
+    b_src->u.b = true;
+    s_src = g_new0(AltStrBool, 1);
+    s_src->type = QTYPE_QSTRING;
+    s_src->u.s = g_strdup("World");
+
+    b_dst = QAPI_CLONE(AltStrBool, b_src);
+    g_assert(b_dst);
+    g_assert_cmpint(b_dst->type, ==, b_src->type);
+    g_assert_cmpint(b_dst->u.b, ==, b_src->u.b);
+    s_dst = QAPI_CLONE(AltStrBool, s_src);
+    g_assert(s_dst);
+    g_assert_cmpint(s_dst->type, ==, s_src->type);
+    g_assert_cmpstr(s_dst->u.s, ==, s_src->u.s);
+    g_assert(s_dst->u.s != s_src->u.s);
+
+    qapi_free_AltStrBool(b_src);
+    qapi_free_AltStrBool(s_src);
+    qapi_free_AltStrBool(b_dst);
+    qapi_free_AltStrBool(s_dst);
+}
+
+static void test_clone_native_list(void)
+{
+    uint8List *src, *dst;
+    uint8List *tmp = NULL;
+    int i;
+
+    /* Build list in reverse */
+    for (i = 10; i; i--) {
+        src = g_new0(uint8List, 1);
+        src->next = tmp;
+        src->value = i;
+        tmp = src;
+    }
+
+    dst = QAPI_CLONE(uint8List, src);
+    for (tmp = dst, i = 1; i <= 10; i++) {
+        g_assert(tmp);
+        g_assert_cmpint(tmp->value, ==, i);
+        tmp = tmp->next;
+    }
+    g_assert(!tmp);
+
+    qapi_free_uint8List(src);
+    qapi_free_uint8List(dst);
+}
+
+static void test_clone_empty(void)
+{
+    Empty2 *src, *dst;
+
+    src = g_new0(Empty2, 1);
+    dst = QAPI_CLONE(Empty2, src);
+    g_assert(dst);
+    qapi_free_Empty2(src);
+    qapi_free_Empty2(dst);
+}
+
+static void test_clone_complex1(void)
+{
+    UserDefNativeListUnion *src, *dst;
+
+    src = g_new0(UserDefNativeListUnion, 1);
+    src->type = USER_DEF_NATIVE_LIST_UNION_KIND_STRING;
+
+    dst = QAPI_CLONE(UserDefNativeListUnion, src);
+    g_assert(dst);
+    g_assert_cmpint(dst->type, ==, src->type);
+    g_assert(!dst->u.string.data);
+
+    qapi_free_UserDefNativeListUnion(src);
+    qapi_free_UserDefNativeListUnion(dst);
+}
+
+static void test_clone_complex2(void)
+{
+    WrapAlternate *src, *dst;
+
+    src = g_new0(WrapAlternate, 1);
+    src->alt = g_new(UserDefAlternate, 1);
+    src->alt->type = QTYPE_QDICT;
+    src->alt->u.udfu.integer = 42;
+    /* Clone intentionally converts NULL into "" for strings */
+    src->alt->u.udfu.string = NULL;
+    src->alt->u.udfu.enum1 = ENUM_ONE_VALUE3;
+    src->alt->u.udfu.u.value3.intb = 99;
+    src->alt->u.udfu.u.value3.has_a_b = true;
+    src->alt->u.udfu.u.value3.a_b = true;
+
+    dst = QAPI_CLONE(WrapAlternate, src);
+    g_assert(dst);
+    g_assert(dst->alt);
+    g_assert_cmpint(dst->alt->type, ==, QTYPE_QDICT);
+    g_assert_cmpint(dst->alt->u.udfu.integer, ==, 42);
+    g_assert_cmpstr(dst->alt->u.udfu.string, ==, "");
+    g_assert_cmpint(dst->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE3);
+    g_assert_cmpint(dst->alt->u.udfu.u.value3.intb, ==, 99);
+    g_assert_cmpint(dst->alt->u.udfu.u.value3.has_a_b, ==, true);
+    g_assert_cmpint(dst->alt->u.udfu.u.value3.a_b, ==, true);
+
+    qapi_free_WrapAlternate(src);
+    qapi_free_WrapAlternate(dst);
+}
+
+static void test_clone_complex3(void)
+{
+    __org_qemu_x_Struct2 *src, *dst;
+    __org_qemu_x_Union1List *tmp;
+
+    src = g_new0(__org_qemu_x_Struct2, 1);
+    tmp = src->array = g_new0(__org_qemu_x_Union1List, 1);
+    tmp->value = g_new0(__org_qemu_x_Union1, 1);
+    tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH;
+    tmp->value->u.__org_qemu_x_branch.data = g_strdup("one");
+    tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1);
+    tmp->value = g_new0(__org_qemu_x_Union1, 1);
+    tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH;
+    tmp->value->u.__org_qemu_x_branch.data = g_strdup("two");
+    tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1);
+    tmp->value = g_new0(__org_qemu_x_Union1, 1);
+    tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH;
+    tmp->value->u.__org_qemu_x_branch.data = g_strdup("three");
+
+    dst = QAPI_CLONE(__org_qemu_x_Struct2, src);
+    g_assert(dst);
+    tmp = dst->array;
+    g_assert(tmp);
+    g_assert(tmp->value);
+    g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "one");
+    tmp = tmp->next;
+    g_assert(tmp);
+    g_assert(tmp->value);
+    g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "two");
+    tmp = tmp->next;
+    g_assert(tmp);
+    g_assert(tmp->value);
+    g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "three");
+    tmp = tmp->next;
+    g_assert(!tmp);
+
+    qapi_free___org_qemu_x_Struct2(src);
+    qapi_free___org_qemu_x_Struct2(dst);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/visitor/clone/struct", test_clone_struct);
+    g_test_add_func("/visitor/clone/alternate", test_clone_alternate);
+    g_test_add_func("/visitor/clone/native_list", test_clone_native_list);
+    g_test_add_func("/visitor/clone/empty", test_clone_empty);
+    g_test_add_func("/visitor/clone/complex1", test_clone_complex1);
+    g_test_add_func("/visitor/clone/complex2", test_clone_complex2);
+    g_test_add_func("/visitor/clone/complex3", test_clone_complex3);
+
+    return g_test_run();
+}
diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c
index d75b114c15..0a9e75f1bb 100644
--- a/tests/test-opts-visitor.c
+++ b/tests/test-opts-visitor.c
@@ -37,16 +37,15 @@ setup_fixture(OptsVisitorFixture *f, gconstpointer test_data)
 {
     const char *opts_string = test_data;
     QemuOpts *opts;
-    OptsVisitor *ov;
+    Visitor *v;
 
     opts = qemu_opts_parse(qemu_find_opts("userdef"), opts_string, false,
                            NULL);
     g_assert(opts != NULL);
 
-    ov = opts_visitor_new(opts);
-    visit_type_UserDefOptions(opts_get_visitor(ov), NULL, &f->userdef,
-                              &f->err);
-    opts_visitor_cleanup(ov);
+    v = opts_visitor_new(opts);
+    visit_type_UserDefOptions(v, NULL, &f->userdef, &f->err);
+    visit_free(v);
     qemu_opts_del(opts);
 }
 
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index e8f619d331..8ffeb045cd 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -216,14 +216,14 @@ static void test_dealloc_partial(void)
     /* create partial object */
     {
         QDict *ud2_dict;
-        QmpInputVisitor *qiv;
+        Visitor *v;
 
         ud2_dict = qdict_new();
         qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));
 
-        qiv = qmp_input_visitor_new(QOBJECT(ud2_dict), true);
-        visit_type_UserDefTwo(qmp_input_get_visitor(qiv), NULL, &ud2, &err);
-        qmp_input_visitor_cleanup(qiv);
+        v = qmp_input_visitor_new(QOBJECT(ud2_dict), true);
+        visit_type_UserDefTwo(v, NULL, &ud2, &err);
+        visit_free(v);
         QDECREF(ud2_dict);
     }
 
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index d5f57c583a..814550ac71 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -19,13 +19,14 @@
 #include "test-qapi-types.h"
 #include "test-qapi-visit.h"
 #include "qapi/qmp/types.h"
+#include "qapi/qmp/qjson.h"
 #include "test-qmp-introspect.h"
 #include "qmp-introspect.h"
 #include "qapi-visit.h"
 
 typedef struct TestInputVisitorData {
     QObject *obj;
-    QmpInputVisitor *qiv;
+    Visitor *qiv;
 } TestInputVisitorData;
 
 static void validate_teardown(TestInputVisitorData *data,
@@ -35,7 +36,7 @@ static void validate_teardown(TestInputVisitorData *data,
     data->obj = NULL;
 
     if (data->qiv) {
-        qmp_input_visitor_cleanup(data->qiv);
+        visit_free(data->qiv);
         data->qiv = NULL;
     }
 }
@@ -47,8 +48,6 @@ static Visitor *validate_test_init_internal(TestInputVisitorData *data,
                                             const char *json_string,
                                             va_list *ap)
 {
-    Visitor *v;
-
     validate_teardown(data, NULL);
 
     data->obj = qobject_from_jsonv(json_string, ap);
@@ -56,11 +55,7 @@ static Visitor *validate_test_init_internal(TestInputVisitorData *data,
 
     data->qiv = qmp_input_visitor_new(data->obj, true);
     g_assert(data->qiv);
-
-    v = qmp_input_get_visitor(data->qiv);
-    g_assert(v);
-
-    return v;
+    return data->qiv;
 }
 
 static GCC_FMT_ATTR(2, 3)
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 1a4585c553..f583dce3ed 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -18,10 +18,11 @@
 #include "test-qapi-types.h"
 #include "test-qapi-visit.h"
 #include "qapi/qmp/types.h"
+#include "qapi/qmp/qjson.h"
 
 typedef struct TestInputVisitorData {
     QObject *obj;
-    QmpInputVisitor *qiv;
+    Visitor *qiv;
 } TestInputVisitorData;
 
 static void visitor_input_teardown(TestInputVisitorData *data,
@@ -31,7 +32,7 @@ static void visitor_input_teardown(TestInputVisitorData *data,
     data->obj = NULL;
 
     if (data->qiv) {
-        qmp_input_visitor_cleanup(data->qiv);
+        visit_free(data->qiv);
         data->qiv = NULL;
     }
 }
@@ -43,8 +44,6 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
                                                  const char *json_string,
                                                  va_list *ap)
 {
-    Visitor *v;
-
     visitor_input_teardown(data, NULL);
 
     data->obj = qobject_from_jsonv(json_string, ap);
@@ -52,11 +51,7 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
 
     data->qiv = qmp_input_visitor_new(data->obj, false);
     g_assert(data->qiv);
-
-    v = qmp_input_get_visitor(data->qiv);
-    g_assert(v);
-
-    return v;
+    return data->qiv;
 }
 
 static GCC_FMT_ATTR(2, 3)
@@ -303,7 +298,7 @@ static void test_visitor_in_null(TestInputVisitorData *data,
     visit_type_null(v, "b", &err);
     error_free_or_abort(&err);
     visit_check_struct(v, &error_abort);
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
 }
 
 static void test_visitor_in_union_flat(TestInputVisitorData *data,
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index f8a7a271a9..513d71f0d1 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -18,28 +18,34 @@
 #include "test-qapi-types.h"
 #include "test-qapi-visit.h"
 #include "qapi/qmp/types.h"
+#include "qapi/qmp/qjson.h"
 
 typedef struct TestOutputVisitorData {
-    QmpOutputVisitor *qov;
     Visitor *ov;
+    QObject *obj;
 } TestOutputVisitorData;
 
 static void visitor_output_setup(TestOutputVisitorData *data,
                                  const void *unused)
 {
-    data->qov = qmp_output_visitor_new();
-    g_assert(data->qov != NULL);
-
-    data->ov = qmp_output_get_visitor(data->qov);
-    g_assert(data->ov != NULL);
+    data->ov = qmp_output_visitor_new(&data->obj);
+    g_assert(data->ov);
 }
 
 static void visitor_output_teardown(TestOutputVisitorData *data,
                                     const void *unused)
 {
-    qmp_output_visitor_cleanup(data->qov);
-    data->qov = NULL;
+    visit_free(data->ov);
     data->ov = NULL;
+    qobject_decref(data->obj);
+    data->obj = NULL;
+}
+
+static QObject *visitor_get(TestOutputVisitorData *data)
+{
+    visit_complete(data->ov, &data->obj);
+    g_assert(data->obj);
+    return data->obj;
 }
 
 static void visitor_reset(TestOutputVisitorData *data)
@@ -56,12 +62,9 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
 
     visit_type_int(data->ov, NULL, &value, &error_abort);
 
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QINT);
     g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, value);
-
-    qobject_decref(obj);
 }
 
 static void test_visitor_out_bool(TestOutputVisitorData *data,
@@ -72,12 +75,9 @@ static void test_visitor_out_bool(TestOutputVisitorData *data,
 
     visit_type_bool(data->ov, NULL, &value, &error_abort);
 
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QBOOL);
     g_assert(qbool_get_bool(qobject_to_qbool(obj)) == value);
-
-    qobject_decref(obj);
 }
 
 static void test_visitor_out_number(TestOutputVisitorData *data,
@@ -88,12 +88,9 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
 
     visit_type_number(data->ov, NULL, &value, &error_abort);
 
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QFLOAT);
     g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == value);
-
-    qobject_decref(obj);
 }
 
 static void test_visitor_out_string(TestOutputVisitorData *data,
@@ -104,12 +101,9 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
 
     visit_type_str(data->ov, NULL, &string, &error_abort);
 
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QSTRING);
     g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string);
-
-    qobject_decref(obj);
 }
 
 static void test_visitor_out_no_string(TestOutputVisitorData *data,
@@ -121,12 +115,9 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data,
     /* A null string should return "" */
     visit_type_str(data->ov, NULL, &string, &error_abort);
 
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QSTRING);
     g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, "");
-
-    qobject_decref(obj);
 }
 
 static void test_visitor_out_enum(TestOutputVisitorData *data,
@@ -138,12 +129,10 @@ static void test_visitor_out_enum(TestOutputVisitorData *data,
     for (i = 0; i < ENUM_ONE__MAX; i++) {
         visit_type_EnumOne(data->ov, "unused", &i, &error_abort);
 
-        obj = qmp_output_get_qobject(data->qov);
-        g_assert(obj != NULL);
+        obj = visitor_get(data);
         g_assert(qobject_type(obj) == QTYPE_QSTRING);
         g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==,
                         EnumOne_lookup[i]);
-        qobject_decref(obj);
         visitor_reset(data);
     }
 }
@@ -176,8 +165,7 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
 
     visit_type_TestStruct(data->ov, NULL, &p, &error_abort);
 
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QDICT);
 
     qdict = qobject_to_qdict(obj);
@@ -185,8 +173,6 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
     g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42);
     g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, false);
     g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo");
-
-    QDECREF(qdict);
 }
 
 static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
@@ -221,8 +207,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
 
     visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort);
 
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QDICT);
 
     qdict = qobject_to_qdict(obj);
@@ -249,7 +234,6 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
     g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
     g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);
 
-    QDECREF(qdict);
     qapi_free_UserDefTwo(ud2);
 }
 
@@ -301,8 +285,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
 
     visit_type_TestStructList(data->ov, NULL, &head, &error_abort);
 
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QLIST);
 
     qlist = qobject_to_qlist(obj);
@@ -323,7 +306,6 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
     }
     g_assert_cmpint(i, ==, max_items);
 
-    QDECREF(qlist);
     qapi_free_TestStructList(head);
 }
 
@@ -367,11 +349,9 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
 
     qobj = QOBJECT(qint_from_int(-42));
     visit_type_any(data->ov, NULL, &qobj, &error_abort);
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QINT);
     g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, -42);
-    qobject_decref(obj);
     qobject_decref(qobj);
 
     visitor_reset(data);
@@ -382,8 +362,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
     qobj = QOBJECT(qdict);
     visit_type_any(data->ov, NULL, &qobj, &error_abort);
     qobject_decref(qobj);
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     qdict = qobject_to_qdict(obj);
     g_assert(qdict);
     qobj = qdict_get(qdict, "integer");
@@ -401,7 +380,6 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
     qstring = qobject_to_qstring(qobj);
     g_assert(qstring);
     g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
-    qobject_decref(obj);
 }
 
 static void test_visitor_out_union_flat(TestOutputVisitorData *data,
@@ -417,7 +395,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
     tmp->u.value1.boolean = true;
 
     visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort);
-    arg = qmp_output_get_qobject(data->qov);
+    arg = visitor_get(data);
 
     g_assert(qobject_type(arg) == QTYPE_QDICT);
     qdict = qobject_to_qdict(arg);
@@ -428,7 +406,6 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
     g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true);
 
     qapi_free_UserDefFlatUnion(tmp);
-    QDECREF(qdict);
 }
 
 static void test_visitor_out_alternate(TestOutputVisitorData *data,
@@ -443,13 +420,12 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     tmp->u.i = 42;
 
     visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
-    arg = qmp_output_get_qobject(data->qov);
+    arg = visitor_get(data);
 
     g_assert(qobject_type(arg) == QTYPE_QINT);
     g_assert_cmpint(qint_get_int(qobject_to_qint(arg)), ==, 42);
 
     qapi_free_UserDefAlternate(tmp);
-    qobject_decref(arg);
 
     visitor_reset(data);
     tmp = g_new0(UserDefAlternate, 1);
@@ -457,13 +433,12 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     tmp->u.s = g_strdup("hello");
 
     visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
-    arg = qmp_output_get_qobject(data->qov);
+    arg = visitor_get(data);
 
     g_assert(qobject_type(arg) == QTYPE_QSTRING);
     g_assert_cmpstr(qstring_get_str(qobject_to_qstring(arg)), ==, "hello");
 
     qapi_free_UserDefAlternate(tmp);
-    qobject_decref(arg);
 
     visitor_reset(data);
     tmp = g_new0(UserDefAlternate, 1);
@@ -474,7 +449,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     tmp->u.udfu.u.value1.boolean = true;
 
     visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
-    arg = qmp_output_get_qobject(data->qov);
+    arg = visitor_get(data);
 
     g_assert_cmpint(qobject_type(arg), ==, QTYPE_QDICT);
     qdict = qobject_to_qdict(arg);
@@ -485,7 +460,6 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true);
 
     qapi_free_UserDefAlternate(tmp);
-    qobject_decref(arg);
 }
 
 static void test_visitor_out_null(TestOutputVisitorData *data,
@@ -498,15 +472,14 @@ static void test_visitor_out_null(TestOutputVisitorData *data,
     visit_start_struct(data->ov, NULL, NULL, 0, &error_abort);
     visit_type_null(data->ov, "a", &error_abort);
     visit_check_struct(data->ov, &error_abort);
-    visit_end_struct(data->ov);
-    arg = qmp_output_get_qobject(data->qov);
+    visit_end_struct(data->ov, NULL);
+    arg = visitor_get(data);
     g_assert(qobject_type(arg) == QTYPE_QDICT);
     qdict = qobject_to_qdict(arg);
     g_assert_cmpint(qdict_size(qdict), ==, 1);
     nil = qdict_get(qdict, "a");
     g_assert(nil);
     g_assert(qobject_type(nil) == QTYPE_QNULL);
-    qobject_decref(arg);
 }
 
 static void init_native_list(UserDefNativeListUnion *cvalue)
@@ -737,10 +710,9 @@ static void test_native_list(TestOutputVisitorData *data,
 
     visit_type_UserDefNativeListUnion(data->ov, NULL, &cvalue, &error_abort);
 
-    obj = qmp_output_get_qobject(data->qov);
+    obj = visitor_get(data);
     check_native_list(obj, cvalue->type);
     qapi_free_UserDefNativeListUnion(cvalue);
-    qobject_decref(obj);
 }
 
 static void test_visitor_out_native_list_int(TestOutputVisitorData *data,
diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c
index 7fe7a9c270..d837ebedad 100644
--- a/tests/test-string-input-visitor.c
+++ b/tests/test-string-input-visitor.c
@@ -20,15 +20,15 @@
 #include "qapi/qmp/types.h"
 
 typedef struct TestInputVisitorData {
-    StringInputVisitor *siv;
+    Visitor *v;
 } TestInputVisitorData;
 
 static void visitor_input_teardown(TestInputVisitorData *data,
                                    const void *unused)
 {
-    if (data->siv) {
-        string_input_visitor_cleanup(data->siv);
-        data->siv = NULL;
+    if (data->v) {
+        visit_free(data->v);
+        data->v = NULL;
     }
 }
 
@@ -39,15 +39,9 @@ static
 Visitor *visitor_input_test_init(TestInputVisitorData *data,
                                  const char *string)
 {
-    Visitor *v;
-
-    data->siv = string_input_visitor_new(string);
-    g_assert(data->siv != NULL);
-
-    v = string_input_get_visitor(data->siv);
-    g_assert(v != NULL);
-
-    return v;
+    data->v = string_input_visitor_new(string);
+    g_assert(data->v);
+    return data->v;
 }
 
 static void test_visitor_in_int(TestInputVisitorData *data,
@@ -199,8 +193,6 @@ static void test_visitor_in_enum(TestInputVisitorData *data,
 
         visitor_input_teardown(data, NULL);
     }
-
-    data->siv = NULL;
 }
 
 /* Try to crash the visitors */
diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c
index edff5235fe..444844a15a 100644
--- a/tests/test-string-output-visitor.c
+++ b/tests/test-string-output-visitor.c
@@ -20,39 +20,53 @@
 #include "qapi/qmp/types.h"
 
 typedef struct TestOutputVisitorData {
-    StringOutputVisitor *sov;
     Visitor *ov;
+    char *str;
     bool human;
 } TestOutputVisitorData;
 
+static void visitor_output_setup_internal(TestOutputVisitorData *data,
+                                          bool human)
+{
+    data->human = human;
+    data->ov = string_output_visitor_new(human, &data->str);
+    g_assert(data->ov);
+}
+
 static void visitor_output_setup(TestOutputVisitorData *data,
                                  const void *unused)
 {
-    data->human = false;
-    data->sov = string_output_visitor_new(data->human);
-    g_assert(data->sov != NULL);
-
-    data->ov = string_output_get_visitor(data->sov);
-    g_assert(data->ov != NULL);
+    return visitor_output_setup_internal(data, false);
 }
 
 static void visitor_output_setup_human(TestOutputVisitorData *data,
                                        const void *unused)
 {
-    data->human = true;
-    data->sov = string_output_visitor_new(data->human);
-    g_assert(data->sov != NULL);
-
-    data->ov = string_output_get_visitor(data->sov);
-    g_assert(data->ov != NULL);
+    return visitor_output_setup_internal(data, true);
 }
 
 static void visitor_output_teardown(TestOutputVisitorData *data,
                                     const void *unused)
 {
-    string_output_visitor_cleanup(data->sov);
-    data->sov = NULL;
+    visit_free(data->ov);
     data->ov = NULL;
+    g_free(data->str);
+    data->str = NULL;
+}
+
+static char *visitor_get(TestOutputVisitorData *data)
+{
+    visit_complete(data->ov, &data->str);
+    g_assert(data->str);
+    return data->str;
+}
+
+static void visitor_reset(TestOutputVisitorData *data)
+{
+    bool human = data->human;
+
+    visitor_output_teardown(data, NULL);
+    visitor_output_setup_internal(data, human);
 }
 
 static void test_visitor_out_int(TestOutputVisitorData *data,
@@ -65,14 +79,12 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
     visit_type_int(data->ov, NULL, &value, &err);
     g_assert(!err);
 
-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     if (data->human) {
         g_assert_cmpstr(str, ==, "42 (0x2a)");
     } else {
         g_assert_cmpstr(str, ==, "42");
     }
-    g_free(str);
 }
 
 static void test_visitor_out_intList(TestOutputVisitorData *data,
@@ -94,8 +106,7 @@ static void test_visitor_out_intList(TestOutputVisitorData *data,
     visit_type_intList(data->ov, NULL, &list, &err);
     g_assert(err == NULL);
 
-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     if (data->human) {
         g_assert_cmpstr(str, ==,
             "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807 "
@@ -105,13 +116,7 @@ static void test_visitor_out_intList(TestOutputVisitorData *data,
         g_assert_cmpstr(str, ==,
             "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807");
     }
-    g_free(str);
-    while (list) {
-        intList *tmp2;
-        tmp2 = list->next;
-        g_free(list);
-        list = tmp2;
-    }
+    qapi_free_intList(list);
 }
 
 static void test_visitor_out_bool(TestOutputVisitorData *data,
@@ -124,10 +129,8 @@ static void test_visitor_out_bool(TestOutputVisitorData *data,
     visit_type_bool(data->ov, NULL, &value, &err);
     g_assert(!err);
 
-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     g_assert_cmpstr(str, ==, "true");
-    g_free(str);
 }
 
 static void test_visitor_out_number(TestOutputVisitorData *data,
@@ -140,10 +143,8 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
     visit_type_number(data->ov, NULL, &value, &err);
     g_assert(!err);
 
-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     g_assert_cmpstr(str, ==, "3.140000");
-    g_free(str);
 }
 
 static void test_visitor_out_string(TestOutputVisitorData *data,
@@ -157,61 +158,50 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
     visit_type_str(data->ov, NULL, &string, &err);
     g_assert(!err);
 
-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     if (data->human) {
         g_assert_cmpstr(str, ==, string_human);
     } else {
         g_assert_cmpstr(str, ==, string);
     }
-    g_free(str);
 }
 
 static void test_visitor_out_no_string(TestOutputVisitorData *data,
                                        const void *unused)
 {
     char *string = NULL;
-    Error *err = NULL;
     char *str;
 
     /* A null string should return "" */
-    visit_type_str(data->ov, NULL, &string, &err);
-    g_assert(!err);
+    visit_type_str(data->ov, NULL, &string, &error_abort);
 
-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     if (data->human) {
         g_assert_cmpstr(str, ==, "<null>");
     } else {
         g_assert_cmpstr(str, ==, "");
     }
-    g_free(str);
 }
 
 static void test_visitor_out_enum(TestOutputVisitorData *data,
                                   const void *unused)
 {
-    Error *err = NULL;
     char *str;
     EnumOne i;
 
     for (i = 0; i < ENUM_ONE__MAX; i++) {
-        char *str_human;
+        visit_type_EnumOne(data->ov, "unused", &i, &error_abort);
 
-        visit_type_EnumOne(data->ov, "unused", &i, &err);
-        g_assert(!err);
-
-        str_human = g_strdup_printf("\"%s\"", EnumOne_lookup[i]);
-
-        str = string_output_get_string(data->sov);
-        g_assert(str != NULL);
+        str = visitor_get(data);
         if (data->human) {
+            char *str_human = g_strdup_printf("\"%s\"", EnumOne_lookup[i]);
+
             g_assert_cmpstr(str, ==, str_human);
+            g_free(str_human);
         } else {
             g_assert_cmpstr(str, ==, EnumOne_lookup[i]);
         }
-        g_free(str_human);
-	g_free(str);
+        visitor_reset(data);
     }
 }
 
@@ -224,8 +214,7 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
     for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
         err = NULL;
         visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
-        g_assert(err);
-        error_free(err);
+        error_free_or_abort(&err);
     }
 }
 
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 777469f114..dba4670762 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -19,6 +19,7 @@
 #include "test-qapi-visit.h"
 #include "qapi/error.h"
 #include "qapi/qmp/types.h"
+#include "qapi/qmp/qjson.h"
 #include "qapi/qmp-input-visitor.h"
 #include "qapi/qmp-output-visitor.h"
 #include "qapi/string-input-visitor.h"
@@ -88,11 +89,11 @@ typedef void (*VisitorFunc)(Visitor *v, void **native, Error **errp);
 
 static void dealloc_helper(void *native_in, VisitorFunc visit, Error **errp)
 {
-    QapiDeallocVisitor *qdv = qapi_dealloc_visitor_new();
+    Visitor *v = qapi_dealloc_visitor_new();
 
-    visit(qapi_dealloc_get_visitor(qdv), &native_in, errp);
+    visit(v, &native_in, errp);
 
-    qapi_dealloc_visitor_cleanup(qdv);
+    visit_free(v);
 }
 
 static void visit_primitive_type(Visitor *v, void **native, Error **errp)
@@ -1011,8 +1012,9 @@ static PrimitiveType pt_values[] = {
 /* visitor-specific op implementations */
 
 typedef struct QmpSerializeData {
-    QmpOutputVisitor *qov;
-    QmpInputVisitor *qiv;
+    Visitor *qov;
+    QObject *obj;
+    Visitor *qiv;
 } QmpSerializeData;
 
 static void qmp_serialize(void *native_in, void **datap,
@@ -1020,8 +1022,8 @@ static void qmp_serialize(void *native_in, void **datap,
 {
     QmpSerializeData *d = g_malloc0(sizeof(*d));
 
-    d->qov = qmp_output_visitor_new();
-    visit(qmp_output_get_visitor(d->qov), &native_in, errp);
+    d->qov = qmp_output_visitor_new(&d->obj);
+    visit(d->qov, &native_in, errp);
     *datap = d;
 }
 
@@ -1032,7 +1034,8 @@ static void qmp_deserialize(void **native_out, void *datap,
     QString *output_json;
     QObject *obj_orig, *obj;
 
-    obj_orig = qmp_output_get_qobject(d->qov);
+    visit_complete(d->qov, &d->obj);
+    obj_orig = d->obj;
     output_json = qobject_to_json(obj_orig);
     obj = qobject_from_json(qstring_get_str(output_json));
 
@@ -1040,22 +1043,22 @@ static void qmp_deserialize(void **native_out, void *datap,
     d->qiv = qmp_input_visitor_new(obj, true);
     qobject_decref(obj_orig);
     qobject_decref(obj);
-    visit(qmp_input_get_visitor(d->qiv), native_out, errp);
+    visit(d->qiv, native_out, errp);
 }
 
 static void qmp_cleanup(void *datap)
 {
     QmpSerializeData *d = datap;
-    qmp_output_visitor_cleanup(d->qov);
-    qmp_input_visitor_cleanup(d->qiv);
+    visit_free(d->qov);
+    visit_free(d->qiv);
 
     g_free(d);
 }
 
 typedef struct StringSerializeData {
     char *string;
-    StringOutputVisitor *sov;
-    StringInputVisitor *siv;
+    Visitor *sov;
+    Visitor *siv;
 } StringSerializeData;
 
 static void string_serialize(void *native_in, void **datap,
@@ -1063,8 +1066,8 @@ static void string_serialize(void *native_in, void **datap,
 {
     StringSerializeData *d = g_malloc0(sizeof(*d));
 
-    d->sov = string_output_visitor_new(false);
-    visit(string_output_get_visitor(d->sov), &native_in, errp);
+    d->sov = string_output_visitor_new(false, &d->string);
+    visit(d->sov, &native_in, errp);
     *datap = d;
 }
 
@@ -1073,17 +1076,17 @@ static void string_deserialize(void **native_out, void *datap,
 {
     StringSerializeData *d = datap;
 
-    d->string = string_output_get_string(d->sov);
+    visit_complete(d->sov, &d->string);
     d->siv = string_input_visitor_new(d->string);
-    visit(string_input_get_visitor(d->siv), native_out, errp);
+    visit(d->siv, native_out, errp);
 }
 
 static void string_cleanup(void *datap)
 {
     StringSerializeData *d = datap;
 
-    string_output_visitor_cleanup(d->sov);
-    string_input_visitor_cleanup(d->siv);
+    visit_free(d->sov);
+    visit_free(d->siv);
     g_free(d->string);
     g_free(d);
 }
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index cc2b043907..fb83d48944 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1143,33 +1143,6 @@ SocketAddress *socket_remote_address(int fd, Error **errp)
     return socket_sockaddr_to_address(&ss, sslen, errp);
 }
 
-
-void qapi_copy_SocketAddress(SocketAddress **p_dest,
-                             SocketAddress *src)
-{
-    QmpOutputVisitor *qov;
-    QmpInputVisitor *qiv;
-    Visitor *ov, *iv;
-    QObject *obj;
-
-    *p_dest = NULL;
-
-    qov = qmp_output_visitor_new();
-    ov = qmp_output_get_visitor(qov);
-    visit_type_SocketAddress(ov, NULL, &src, &error_abort);
-    obj = qmp_output_get_qobject(qov);
-    qmp_output_visitor_cleanup(qov);
-    if (!obj) {
-        return;
-    }
-
-    qiv = qmp_input_visitor_new(obj, true);
-    iv = qmp_input_get_visitor(qiv);
-    visit_type_SocketAddress(iv, NULL, p_dest, &error_abort);
-    qmp_input_visitor_cleanup(qiv);
-    qobject_decref(obj);
-}
-
 char *socket_address_to_string(struct SocketAddress *addr, Error **errp)
 {
     char *buf;