[pulseaudio-commits] [Git][pulseaudio/pulseaudio][master] 5 commits: json: improve supported numbers

PulseAudio Marge Bot gitlab at gitlab.freedesktop.org
Sat Mar 27 11:21:31 UTC 2021



PulseAudio Marge Bot pushed to branch master at PulseAudio / pulseaudio


Commits:
1df4a311 by Igor V. Kovalenko at 2021-03-27T11:18:22+00:00
json: improve supported numbers

Use 64bit signed integers and fix double value conversion.

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>

- - - - -
0ba768b2 by Igor V. Kovalenko at 2021-03-27T11:18:22+00:00
json: add JSON encoder

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>

- - - - -
1dd05f4a by Igor V. Kovalenko at 2021-03-27T11:18:22+00:00
message-params: use JSON instead of custom format

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>

- - - - -
a2db3fcf by Igor V. Kovalenko at 2021-03-27T11:18:22+00:00
message-params: clean up

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>

- - - - -
7ca50bab by Igor V. Kovalenko at 2021-03-27T11:18:22+00:00
bluetooth: update messaging api reference

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>

- - - - -


15 changed files:

- doc/messaging_api.txt
- doxygen/doxygen.conf.in
- src/meson.build
- src/modules/bluetooth/module-bluez5-device.c
- src/pulse/json.c
- src/pulse/json.h
- src/pulse/map-file
- src/pulse/meson.build
- − src/pulse/message-params.c
- − src/pulse/message-params.h
- src/pulsecore/core.c
- src/pulsecore/message-handler.c
- src/pulsecore/message-handler.h
- src/tests/json-test.c
- src/utils/pactl.c


Changes:

=====================================
doc/messaging_api.txt
=====================================
@@ -4,26 +4,34 @@ The message API allows any object within pulseaudio to register a message
 handler. A message handler is a function that can be called by clients using
 PA_COMMAND_SEND_OBJECT_MESSAGE. A message consists at least of an object path
 and a message command, both specified as strings. Additional parameters can
-be specified using a single string, but are not mandatory. The message handler
-returns an error number as defined in def.h and also returns a string in
-the "response" variable. If the string is not empty it consists of elements.
-Curly braces are used to separate elements. Each element can itself contain
-further elements. For example consider a message that returns multiple elements
-which each contain an integer and an array of float. A response string would
-look like that:
-{{Integer} {{1st float} {2nd float} ...}}{...}
-Any characters that are not enclosed in curly braces are ignored (all characters
-between { and {, between } and } and between } and {). The same syntax is used
-to specify message parameters. The reference further down lists available messages,
-their parameters and return values. If a return value is enclosed in {}, this
-means that multiple elements of the same type may be returned.
-
-For string parameters that contain curly braces or backslashes, those characters
-must be escaped by adding a "\" before them.
+be specified using a single string in JSON format, but are not mandatory..
+
+The message handler returns an error number as defined in def.h and also returns
+a string in the "response" variable. Non-empty response will be in JSON format.
+
+The reference further down lists available messages, their parameters
+and return values.
 
 Reference:
 
 Object path: /core
 Message: list-handlers
 Parameters: None
-Return value: {{{Handler name} {Description}} ...}
+Return value: JSON array of handler description objects
+    [{"name":"Handler name","description":"Description"} ...]
+
+Object path: /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez
+Message: list-codecs
+Parameters: None
+Return value: JSON array of codec description objects
+    [{"name":"codec1","description":"Codec 1"} ...]
+
+Object path: /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez
+Message: get-codec
+Parameters: None
+Return value: "codec name"
+
+Object path: /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez
+Message: switch-codec
+Parameters: "codec name"
+Return value: none


=====================================
doxygen/doxygen.conf.in
=====================================
@@ -683,7 +683,6 @@ INPUT                  = @top_srcdir@/src/pulse/channelmap.h \
                          @top_srcdir@/src/pulse/mainloop-api.h \
                          @top_srcdir@/src/pulse/mainloop-signal.h \
                          @top_srcdir@/src/pulse/mainloop.h \
-                         @top_srcdir@/src/pulse/message-params.h \
                          @top_srcdir@/src/pulse/operation.h \
                          @top_srcdir@/src/pulse/proplist.h \
                          @top_srcdir@/src/pulse/pulseaudio.h \


=====================================
src/meson.build
=====================================
@@ -5,7 +5,6 @@ libpulsecommon_sources = [
   'pulse/format.c',
   'pulse/json.c',
   'pulse/mainloop-api.c',
-  'pulse/message-params.c',
   'pulse/xmalloc.c',
   'pulse/proplist.c',
   'pulse/utf8.c',
@@ -76,7 +75,6 @@ libpulsecommon_headers = [
   'pulse/format.h',
   'pulse/json.h',
   'pulse/mainloop-api.h',
-  'pulse/message-params.h',
   'pulse/xmalloc.h',
   'pulse/proplist.h',
   'pulse/utf8.h',


=====================================
src/modules/bluetooth/module-bluez5-device.c
=====================================
@@ -27,11 +27,11 @@
 
 #include <arpa/inet.h>
 
+#include <pulse/json.h>
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
 #include <pulse/utf8.h>
 #include <pulse/util.h>
-#include <pulse/message-params.h>
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/core-rtclock.h>
@@ -2458,7 +2458,7 @@ static char *list_codecs(struct userdata *u) {
     const pa_a2dp_codec_capabilities *a2dp_capabilities;
     const pa_a2dp_codec_id *key;
     pa_hashmap *a2dp_endpoints;
-    pa_message_params *param;
+    pa_json_encoder *encoder;
     unsigned int i;
     bool is_a2dp_sink;
     void *state;
@@ -2467,9 +2467,9 @@ static char *list_codecs(struct userdata *u) {
 
     a2dp_endpoints = is_a2dp_sink ? u->device->a2dp_sink_endpoints : u->device->a2dp_source_endpoints;
 
-    param = pa_message_params_new();
+    encoder = pa_json_encoder_new();
 
-    pa_message_params_begin_list(param);
+    pa_json_encoder_begin_element_array(encoder);
 
     PA_HASHMAP_FOREACH_KV(key, a2dp_capabilities, a2dp_endpoints, state) {
         for (i = 0; i < pa_bluetooth_a2dp_codec_count(); i++) {
@@ -2479,23 +2479,23 @@ static char *list_codecs(struct userdata *u) {
 
             if (memcmp(key, &a2dp_codec->id, sizeof(pa_a2dp_codec_id)) == 0) {
                 if (a2dp_codec->can_be_supported(is_a2dp_sink)) {
-                    pa_message_params_begin_list(param);
+                    pa_json_encoder_begin_element_object(encoder);
 
-                    pa_message_params_write_string(param, a2dp_codec->name);
-                    pa_message_params_write_string(param, a2dp_codec->description);
+                    pa_json_encoder_add_member_string(encoder, "name", a2dp_codec->name);
+                    pa_json_encoder_add_member_string(encoder, "description", a2dp_codec->description);
 
-                    pa_message_params_end_list(param);
+                    pa_json_encoder_end_object(encoder);
                 }
             }
         }
     }
 
-    pa_message_params_end_list(param);
+    pa_json_encoder_end_array(encoder);
 
-    return pa_message_params_to_string_free(param);
+    return pa_json_encoder_to_string_free(encoder);
 }
 
-static int bluez5_device_message_handler(const char *object_path, const char *message, char *message_parameters, char **response, void *userdata) {
+static int bluez5_device_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
     char *message_handler_path;
     pa_hashmap *capabilities_hashmap;
     pa_bluetooth_profile_t profile;
@@ -2503,8 +2503,6 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
     const char *codec_name;
     struct userdata *u;
     bool is_a2dp_sink;
-    void *state = NULL;
-    int err;
 
     pa_assert(u = (struct userdata *)userdata);
     pa_assert(message);
@@ -2539,9 +2537,17 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
     }
 
     if (pa_streq(message, "switch-codec")) {
-        err = pa_message_params_read_string(message_parameters, &codec_name, &state);
-        if (err < 0)
-            return err;
+        if (!parameters) {
+            pa_log_info("Codec switching operation requires codec name string parameter");
+            return -PA_ERR_INVALID;
+        }
+
+        if (pa_json_object_get_type(parameters) != PA_JSON_TYPE_STRING) {
+            pa_log_info("Codec name object parameter must be a string");
+            return -PA_ERR_INVALID;
+        }
+
+        codec_name = pa_json_object_get_string(parameters);
 
         if (u->a2dp_codec && pa_streq(codec_name, u->a2dp_codec->name)) {
             pa_log_info("Requested codec is currently selected codec");
@@ -2599,15 +2605,13 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
         *response = list_codecs(u);
         return PA_OK;
     } else if (pa_streq(message, "get-codec")) {
-        pa_message_params *param;
-        param = pa_message_params_new();
+        pa_json_encoder *encoder;
+        encoder = pa_json_encoder_new();
 
         if (u->a2dp_codec)
-            pa_message_params_write_string(param, u->a2dp_codec->name);
-        else
-            pa_message_params_write_string(param, "none");
+            pa_json_encoder_add_element_string(encoder, u->a2dp_codec->name);
 
-        *response = pa_message_params_to_string_free(param);
+        *response = pa_json_encoder_to_string_free(encoder);
 
         return PA_OK;
     }


=====================================
src/pulse/json.c
=====================================
@@ -35,7 +35,7 @@ struct pa_json_object {
     pa_json_type type;
 
     union {
-        int int_value;
+        int64_t int_value;
         double double_value;
         bool bool_value;
         char *string_value;
@@ -44,6 +44,30 @@ struct pa_json_object {
     };
 };
 
+/* JSON encoder context type */
+typedef enum pa_json_context_type {
+    /* Top-level context of empty encoder. JSON element can be added. */
+    PA_JSON_CONTEXT_EMPTY  = 0,
+    /* Top-level context of encoder with an element. JSON element cannot be added. */
+    PA_JSON_CONTEXT_TOP    = 1,
+    /* JSON array context. JSON elements can be added. */
+    PA_JSON_CONTEXT_ARRAY  = 2,
+    /* JSON object context. JSON object members can be added. */
+    PA_JSON_CONTEXT_OBJECT = 3,
+} pa_json_context_type_t;
+
+typedef struct encoder_context {
+    pa_json_context_type_t type;
+    int counter;
+    struct encoder_context *next;
+} encoder_context;
+
+/* JSON encoder structure, a wrapper for pa_strbuf and encoder context */
+struct pa_json_encoder {
+    pa_strbuf *buffer;
+    encoder_context *context;
+};
+
 static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth);
 
 static pa_json_object* json_object_new(void) {
@@ -194,33 +218,22 @@ error:
 }
 
 static const char* parse_number(const char *str, pa_json_object *obj) {
-    bool negative = false, has_fraction = false, has_exponent = false, valid = false;
-    unsigned int integer = 0;
-    unsigned int fraction = 0;
-    unsigned int fraction_digits = 0;
-    int exponent = 0;
-
-    if (*str == '-') {
-        negative = true;
-        str++;
-    }
+    bool has_fraction = false, has_exponent = false, valid = false;
+    char *candidate = NULL;
+    const char *s = str;
 
-    if (*str == '0') {
+    if (*s == '-')
+        s++;
+
+    if (*s == '0') {
         valid = true;
-        str++;
+        s++;
         goto fraction;
     }
 
-    while (is_digit(*str)) {
+    while (is_digit(*s)) {
         valid = true;
-
-        if (integer > ((negative ? INT_MAX : UINT_MAX) / 10)) {
-            pa_log("Integer overflow while parsing number");
-            goto error;
-        }
-
-        integer = (integer * 10) + (*str - '0');
-        str++;
+        s++;
     }
 
 fraction:
@@ -230,22 +243,14 @@ fraction:
         goto error;
     }
 
-    if (*str == '.') {
+    if (*s == '.') {
         has_fraction = true;
-        str++;
+        s++;
         valid = false;
 
-        while (is_digit(*str)) {
+        while (is_digit(*s)) {
             valid = true;
-
-            if (fraction > (UINT_MAX / 10)) {
-                pa_log("Integer overflow while parsing fractional part of number");
-                goto error;
-            }
-
-            fraction = (fraction * 10) + (*str - '0');
-            fraction_digits++;
-            str++;
+            s++;
         }
 
         if (!valid) {
@@ -254,52 +259,50 @@ fraction:
         }
     }
 
-    if (*str == 'e' || *str == 'E') {
-        bool exponent_negative = false;
-
+    if (*s == 'e' || *s == 'E') {
         has_exponent = true;
-        str++;
+        s++;
         valid = false;
 
-        if (*str == '-') {
-            exponent_negative = true;
-            str++;
-        } else if (*str == '+')
-            str++;
+        if (*s == '-' || *s == '+')
+            s++;
 
-        while (is_digit(*str)) {
+        while (is_digit(*s)) {
             valid = true;
-
-            if (exponent > (INT_MAX / 10)) {
-                pa_log("Integer overflow while parsing exponent part of number");
-                goto error;
-            }
-
-            exponent = (exponent * 10) + (*str - '0');
-            str++;
+            s++;
         }
 
         if (!valid) {
             pa_log("No digit in exponent while parsing fraction");
             goto error;
         }
-
-        if (exponent_negative)
-            exponent *= -1;
     }
 
+    /* Number format looks good, now try to extract the value.
+     * Here 's' points just after the string which will be consumed. */
+
+    candidate = pa_xstrndup(str, s - str);
+
     if (has_fraction || has_exponent) {
+        if (pa_atod(candidate, &obj->double_value) < 0) {
+            pa_log("Cannot convert string '%s' to double value", str);
+            goto error;
+        }
         obj->type = PA_JSON_TYPE_DOUBLE;
-        obj->double_value =
-            (negative ? -1.0 : 1.0) * (integer + (double) fraction / pow(10, fraction_digits)) * pow(10, exponent);
     } else {
+        if (pa_atoi64(candidate, &obj->int_value) < 0) {
+            pa_log("Cannot convert string '%s' to int64_t value", str);
+            goto error;
+        }
         obj->type = PA_JSON_TYPE_INT;
-        obj->int_value = (negative ? -1 : 1) * integer;
     }
 
-    return str;
+    pa_xfree(candidate);
+
+    return s;
 
 error:
+    pa_xfree(candidate);
     return NULL;
 }
 
@@ -522,7 +525,7 @@ void pa_json_object_free(pa_json_object *obj) {
     pa_xfree(obj);
 }
 
-int pa_json_object_get_int(const pa_json_object *o) {
+int64_t pa_json_object_get_int(const pa_json_object *o) {
     pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
     return o->int_value;
 }
@@ -547,6 +550,11 @@ const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o,
     return pa_hashmap_get(o->object_values, name);
 }
 
+const pa_hashmap *pa_json_object_get_object_member_hashmap(const pa_json_object *o) {
+    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+    return o->object_values;
+}
+
 int pa_json_object_get_array_length(const pa_json_object *o) {
     pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
     return pa_idxset_size(o->array_values);
@@ -612,3 +620,443 @@ bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) {
             pa_assert_not_reached();
     }
 }
+
+/* Write functions. The functions are wrapper functions around pa_strbuf,
+ * so that the client does not need to use pa_strbuf directly. */
+
+static void json_encoder_context_push(pa_json_encoder *encoder, pa_json_context_type_t type) {
+    pa_assert(encoder);
+
+    encoder_context *head = pa_xnew0(encoder_context, 1);
+    head->type = type;
+    head->next = encoder->context;
+    encoder->context = head;
+}
+
+/* Returns type of context popped off encoder context stack. */
+static pa_json_context_type_t json_encoder_context_pop(pa_json_encoder *encoder) {
+    encoder_context *head;
+    pa_json_context_type_t type;
+
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+
+    type = encoder->context->type;
+
+    head = encoder->context->next;
+    pa_xfree(encoder->context);
+    encoder->context = head;
+
+    return type;
+}
+
+pa_json_encoder *pa_json_encoder_new(void) {
+    pa_json_encoder *encoder;
+
+    encoder = pa_xnew(pa_json_encoder, 1);
+    encoder->buffer = pa_strbuf_new();
+
+    encoder->context = NULL;
+    json_encoder_context_push(encoder, PA_JSON_CONTEXT_EMPTY);
+
+    return encoder;
+}
+
+void pa_json_encoder_free(pa_json_encoder *encoder) {
+    pa_json_context_type_t type;
+    pa_assert(encoder);
+
+    /* should have exactly one encoder context left at this point */
+    pa_assert(encoder->context);
+    type = json_encoder_context_pop(encoder);
+    pa_assert(encoder->context == NULL);
+
+    pa_assert(type == PA_JSON_CONTEXT_TOP || type == PA_JSON_CONTEXT_EMPTY);
+    if (type == PA_JSON_CONTEXT_EMPTY)
+        pa_log_warn("JSON encoder is empty.");
+
+    if (encoder->buffer)
+        pa_strbuf_free(encoder->buffer);
+
+    pa_xfree(encoder);
+}
+
+char *pa_json_encoder_to_string_free(pa_json_encoder *encoder) {
+    char *result;
+
+    pa_assert(encoder);
+
+    result = pa_strbuf_to_string_free(encoder->buffer);
+
+    encoder->buffer = NULL;
+    pa_json_encoder_free(encoder);
+
+    return result;
+}
+
+static void json_encoder_insert_delimiter(pa_json_encoder *encoder) {
+    pa_assert(encoder);
+
+    if (encoder->context->counter++)
+        pa_strbuf_putc(encoder->buffer, ',');
+}
+
+/* Escapes p to create valid JSON string.
+ * The caller has to free the returned string. */
+static char *pa_json_escape(const char *p) {
+    const char *s;
+    char *out_string, *output;
+    int char_count = strlen(p);
+
+    /* Maximum number of characters in output string
+     * including trailing 0. */
+    char_count = 2 * char_count + 1;
+
+    /* allocate output string */
+    out_string = pa_xmalloc(char_count);
+    output = out_string;
+
+    /* write output string */
+    for (s = p; *s; ++s) {
+        switch (*s) {
+            case '"':
+                *output++ = '\\';
+                *output++ = '"';
+                break;
+            case '\\':
+                *output++ = '\\';
+                *output++ = '\\';
+                break;
+            case '\b':
+                *output++ = '\\';
+                *output++ = 'b';
+                break;
+
+            case '\f':
+                *output++ = '\\';
+                *output++ = 'f';
+                break;
+
+            case '\n':
+                *output++ = '\\';
+                *output++ = 'n';
+                break;
+
+            case '\r':
+                *output++ = '\\';
+                *output++ = 'r';
+                break;
+
+            case '\t':
+                *output++ = '\\';
+                *output++ = 't';
+                break;
+            default:
+                if (*s < 0x20 || *s > 0x7E) {
+                    pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *s);
+                    pa_xfree(out_string);
+                    return NULL;
+                }
+                *output++ = *s;
+                break;
+        }
+    }
+
+    *output = 0;
+
+    return out_string;
+}
+
+static void json_write_string_escaped(pa_json_encoder *encoder, const char *value) {
+    char *escaped_value;
+
+    pa_assert(encoder);
+
+    escaped_value = pa_json_escape(value);
+    pa_strbuf_printf(encoder->buffer, "\"%s\"", escaped_value);
+    pa_xfree(escaped_value);
+}
+
+/* Writes an opening curly brace */
+void pa_json_encoder_begin_element_object(pa_json_encoder *encoder) {
+    pa_assert(encoder);
+    pa_assert(encoder->context->type != PA_JSON_CONTEXT_TOP);
+
+    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+        encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+    json_encoder_insert_delimiter(encoder);
+    pa_strbuf_putc(encoder->buffer, '{');
+
+    json_encoder_context_push(encoder, PA_JSON_CONTEXT_OBJECT);
+}
+
+/* Writes an opening curly brace */
+void pa_json_encoder_begin_member_object(pa_json_encoder *encoder, const char *name) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+    pa_assert(name && name[0]);
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_string_escaped(encoder, name);
+    pa_strbuf_putc(encoder->buffer, ':');
+
+    pa_strbuf_putc(encoder->buffer, '{');
+
+    json_encoder_context_push(encoder, PA_JSON_CONTEXT_OBJECT);
+}
+
+/* Writes a closing curly brace */
+void pa_json_encoder_end_object(pa_json_encoder *encoder) {
+    pa_json_context_type_t type;
+    pa_assert(encoder);
+
+    type = json_encoder_context_pop(encoder);
+    pa_assert(type == PA_JSON_CONTEXT_OBJECT);
+
+    pa_strbuf_putc(encoder->buffer, '}');
+}
+
+/* Writes an opening bracket */
+void pa_json_encoder_begin_element_array(pa_json_encoder *encoder) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type != PA_JSON_CONTEXT_TOP);
+
+    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+        encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+    json_encoder_insert_delimiter(encoder);
+    pa_strbuf_putc(encoder->buffer, '[');
+
+    json_encoder_context_push(encoder, PA_JSON_CONTEXT_ARRAY);
+}
+
+/* Writes member name and an opening bracket */
+void pa_json_encoder_begin_member_array(pa_json_encoder *encoder, const char *name) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+    pa_assert(name && name[0]);
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_string_escaped(encoder, name);
+    pa_strbuf_putc(encoder->buffer, ':');
+
+    pa_strbuf_putc(encoder->buffer, '[');
+
+    json_encoder_context_push(encoder, PA_JSON_CONTEXT_ARRAY);
+}
+
+/* Writes a closing bracket */
+void pa_json_encoder_end_array(pa_json_encoder *encoder) {
+    pa_json_context_type_t type;
+    pa_assert(encoder);
+
+    type = json_encoder_context_pop(encoder);
+    pa_assert(type == PA_JSON_CONTEXT_ARRAY);
+
+    pa_strbuf_putc(encoder->buffer, ']');
+}
+
+void pa_json_encoder_add_element_string(pa_json_encoder *encoder, const char *value) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+        encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_string_escaped(encoder, value);
+}
+
+void pa_json_encoder_add_member_string(pa_json_encoder *encoder, const char *name, const char *value) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+    pa_assert(name && name[0]);
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_string_escaped(encoder, name);
+
+    pa_strbuf_putc(encoder->buffer, ':');
+
+    /* Null value is written as empty element */
+    if (!value)
+        value = "";
+
+    json_write_string_escaped(encoder, value);
+}
+
+static void json_write_null(pa_json_encoder *encoder) {
+    pa_assert(encoder);
+
+    pa_strbuf_puts(encoder->buffer, "null");
+}
+
+void pa_json_encoder_add_element_null(pa_json_encoder *encoder) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+        encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_null(encoder);
+}
+
+void pa_json_encoder_add_member_null(pa_json_encoder *encoder, const char *name) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+    pa_assert(name && name[0]);
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_string_escaped(encoder, name);
+    pa_strbuf_putc(encoder->buffer, ':');
+
+    json_write_null(encoder);
+}
+
+static void json_write_bool(pa_json_encoder *encoder, bool value) {
+    pa_assert(encoder);
+
+    pa_strbuf_puts(encoder->buffer, value ? "true" : "false");
+}
+
+void pa_json_encoder_add_element_bool(pa_json_encoder *encoder, bool value) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+        encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_bool(encoder, value);
+}
+
+void pa_json_encoder_add_member_bool(pa_json_encoder *encoder, const char *name, bool value) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+    pa_assert(name && name[0]);
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_string_escaped(encoder, name);
+
+    pa_strbuf_putc(encoder->buffer, ':');
+
+    json_write_bool(encoder, value);
+}
+
+static void json_write_int(pa_json_encoder *encoder, int64_t value) {
+    pa_assert(encoder);
+
+    pa_strbuf_printf(encoder->buffer, "%"PRId64, value);
+}
+
+void pa_json_encoder_add_element_int(pa_json_encoder *encoder, int64_t value) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+        encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_int(encoder, value);
+}
+
+void pa_json_encoder_add_member_int(pa_json_encoder *encoder, const char *name, int64_t value) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+    pa_assert(name && name[0]);
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_string_escaped(encoder, name);
+
+    pa_strbuf_putc(encoder->buffer, ':');
+
+    json_write_int(encoder, value);
+}
+
+static void json_write_double(pa_json_encoder *encoder, double value, int precision) {
+    pa_assert(encoder);
+    pa_strbuf_printf(encoder->buffer, "%.*f",  precision, value);
+}
+
+void pa_json_encoder_add_element_double(pa_json_encoder *encoder, double value, int precision) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+        encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_double(encoder, value, precision);
+}
+
+void pa_json_encoder_add_member_double(pa_json_encoder *encoder, const char *name, double value, int precision) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+    pa_assert(name && name[0]);
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_string_escaped(encoder, name);
+
+    pa_strbuf_putc(encoder->buffer, ':');
+
+    json_write_double(encoder, value, precision);
+}
+
+static void json_write_raw(pa_json_encoder *encoder, const char *raw_string) {
+    pa_assert(encoder);
+    pa_strbuf_puts(encoder->buffer, raw_string);
+}
+
+void pa_json_encoder_add_element_raw_json(pa_json_encoder *encoder, const char *raw_json_string) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+        encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_raw(encoder, raw_json_string);
+}
+
+void pa_json_encoder_add_member_raw_json(pa_json_encoder *encoder, const char *name, const char *raw_json_string) {
+    pa_assert(encoder);
+    pa_assert(encoder->context);
+    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+    pa_assert(name && name[0]);
+
+    json_encoder_insert_delimiter(encoder);
+
+    json_write_string_escaped(encoder, name);
+
+    pa_strbuf_putc(encoder->buffer, ':');
+
+    json_write_raw(encoder, raw_json_string);
+}


=====================================
src/pulse/json.h
=====================================
@@ -17,7 +17,11 @@
   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#pragma once
+
 #include <stdbool.h>
+#include <stdint.h>
+#include <pulsecore/hashmap.h>
 
 #define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001)
 
@@ -40,14 +44,67 @@ void pa_json_object_free(pa_json_object *obj);
 
 /* All pointer members that are returned are valid while the corresponding object is valid */
 
-int pa_json_object_get_int(const pa_json_object *o);
+int64_t pa_json_object_get_int(const pa_json_object *o);
 double pa_json_object_get_double(const pa_json_object *o);
 bool pa_json_object_get_bool(const pa_json_object *o);
 const char* pa_json_object_get_string(const pa_json_object *o);
 
 const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name);
 
+/** Returns pa_hashmap (char* -> const pa_json_object*) to iterate over object members. \since 15.0 */
+const pa_hashmap *pa_json_object_get_object_member_hashmap(const pa_json_object *o);
+
 int pa_json_object_get_array_length(const pa_json_object *o);
 const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index);
 
 bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2);
+
+/** @{ \name Write functions */
+
+/** Structure which holds a JSON encoder. Wrapper for pa_strbuf and encoder context. \since 15.0 */
+typedef struct pa_json_encoder pa_json_encoder;
+
+/** Create a new pa_json_encoder structure. \since 15.0 */
+pa_json_encoder *pa_json_encoder_new(void);
+/** Free a pa_json_encoder structure. \since 15.0 */
+void pa_json_encoder_free(pa_json_encoder *encoder);
+/** Convert pa_json_encoder to string, free pa_json_encoder structure.
+ * The returned string needs to be freed with pa_xree(). \since 15.0 */
+char *pa_json_encoder_to_string_free(pa_json_encoder *encoder);
+
+/** Start appending JSON object element by writing an opening brace. \since 15.0 */
+void pa_json_encoder_begin_element_object(pa_json_encoder *encoder);
+/** Start appending JSON object member to JSON object. \since 15.0 */
+void pa_json_encoder_begin_member_object(pa_json_encoder *encoder, const char *name);
+/** End appending JSON object element or member to JSON object. \since 15.0 */
+void pa_json_encoder_end_object(pa_json_encoder *encoder);
+/** Start appending JSON array element by writing an opening bracket. \since 15.0 */
+void pa_json_encoder_begin_element_array(pa_json_encoder *encoder);
+/** Start appending JSON array member to JSON object. \since 15.0 */
+void pa_json_encoder_begin_member_array(pa_json_encoder *encoder, const char *name);
+/** End appending JSON array element or member to JSON object. \since 15.0 */
+void pa_json_encoder_end_array(pa_json_encoder *encoder);
+/** Append null element to JSON. \since 15.0 */
+void pa_json_encoder_add_element_null(pa_json_encoder *encoder);
+/** Append null member to JSON object. \since 15.0 */
+void pa_json_encoder_add_member_null(pa_json_encoder *encoder, const char *name);
+/** Append boolean element to JSON. \since 15.0 */
+void pa_json_encoder_add_element_bool(pa_json_encoder *encoder, bool value);
+/** Append boolean member to JSON object. \since 15.0 */
+void pa_json_encoder_add_member_bool(pa_json_encoder *encoder, const char *name, bool value);
+/** Append string element to JSON. Value will be escaped. \since 15.0 */
+void pa_json_encoder_add_element_string(pa_json_encoder *encoder, const char *value);
+/** Append string member to JSON object. Value will be escaped. \since 15.0 */
+void pa_json_encoder_add_member_string(pa_json_encoder *encoder, const char *name, const char *value);
+/** Append integer element to JSON. \since 15.0 */
+void pa_json_encoder_add_element_int(pa_json_encoder *encoder, int64_t value);
+/** Append integer member to JSON object. \since 15.0 */
+void pa_json_encoder_add_member_int(pa_json_encoder *encoder, const char *name, int64_t value);
+/** Append double element to JSON. \since 15.0 */
+void pa_json_encoder_add_element_double(pa_json_encoder *encoder, double value, int precision);
+/** Append double member to JSON object. \since 15.0 */
+void pa_json_encoder_add_member_double(pa_json_encoder *encoder, const char *name, double value, int precision);
+/** Append raw json string element to JSON. String will be written as is. \since 15.0 */
+void pa_json_encoder_add_element_raw_json(pa_json_encoder *encoder, const char *raw_json_string);
+/** Append raw json string member to JSON object. String will be written as is. \since 15.0 */
+void pa_json_encoder_add_member_raw_json(pa_json_encoder *encoder, const char *name, const char *raw_json_string);


=====================================
src/pulse/map-file
=====================================
@@ -215,6 +215,39 @@ pa_gettimeofday;
 pa_glib_mainloop_free;
 pa_glib_mainloop_get_api;
 pa_glib_mainloop_new;
+pa_json_encoder_add_element_bool;
+pa_json_encoder_add_element_double;
+pa_json_encoder_add_element_int;
+pa_json_encoder_add_element_null;
+pa_json_encoder_add_element_raw_json;
+pa_json_encoder_add_element_string;
+pa_json_encoder_add_member_bool;
+pa_json_encoder_add_member_double;
+pa_json_encoder_add_member_int;
+pa_json_encoder_add_member_null;
+pa_json_encoder_add_member_raw_json;
+pa_json_encoder_add_member_string;
+pa_json_encoder_begin_element_array;
+pa_json_encoder_begin_element_object;
+pa_json_encoder_begin_member_array;
+pa_json_encoder_begin_member_object;
+pa_json_encoder_end_array;
+pa_json_encoder_end_object;
+pa_json_encoder_free;
+pa_json_encoder_new;
+pa_json_encoder_to_string_free;
+pa_json_object_equal;
+pa_json_object_free;
+pa_json_object_get_array_length;
+pa_json_object_get_array_member;
+pa_json_object_get_bool;
+pa_json_object_get_double;
+pa_json_object_get_int;
+pa_json_object_get_object_member;
+pa_json_object_get_object_member_hashmap;
+pa_json_object_get_string;
+pa_json_object_get_type;
+pa_json_parse;
 pa_locale_to_utf8;
 pa_mainloop_api_once;
 pa_mainloop_dispatch;
@@ -229,27 +262,6 @@ pa_mainloop_quit;
 pa_mainloop_run;
 pa_mainloop_set_poll_func;
 pa_mainloop_wakeup;
-pa_message_params_begin_list;
-pa_message_params_end_list;
-pa_message_params_free;
-pa_message_params_new;
-pa_message_params_read_bool;
-pa_message_params_read_double;
-pa_message_params_read_double_array;
-pa_message_params_read_int64;
-pa_message_params_read_int64_array;
-pa_message_params_read_raw;
-pa_message_params_read_string;
-pa_message_params_read_string_array;
-pa_message_params_read_uint64;
-pa_message_params_read_uint64_array;
-pa_message_params_to_string_free;
-pa_message_params_write_bool;
-pa_message_params_write_double;
-pa_message_params_write_int64;
-pa_message_params_write_raw;
-pa_message_params_write_string;
-pa_message_params_write_uint64;
 pa_msleep;
 pa_operation_cancel;
 pa_operation_get_state;


=====================================
src/pulse/meson.build
=====================================
@@ -19,7 +19,6 @@ libpulse_sources = [
   'mainloop-api.c',
   'mainloop-signal.c',
   'mainloop.c',
-  'message-params.c',
   'operation.c',
   'proplist.c',
   'rtclock.c',
@@ -48,10 +47,10 @@ libpulse_headers = [
   'format.h',
   'gccmacro.h',
   'introspect.h',
+  'json.h',
   'mainloop-api.h',
   'mainloop-signal.h',
   'mainloop.h',
-  'message-params.h',
   'operation.h',
   'proplist.h',
   'pulseaudio.h',


=====================================
src/pulse/message-params.c deleted
=====================================
@@ -1,641 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-
-  PulseAudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <locale.h>
-#include <sys/types.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/macro.h>
-#include <pulsecore/strbuf.h>
-#include <pulsecore/core-util.h>
-
-#include "message-params.h"
-
-/* Message parameter structure, a wrapper for pa_strbuf */
-struct pa_message_params {
-    pa_strbuf *buffer;
-};
-
-/* Helper functions */
-
-/* Count number of top level elements in parameter list */
-static int count_elements(const char *c) {
-    const char *s;
-    uint32_t element_count;
-    bool found_element, found_backslash;
-    int open_braces;
-
-    if (!c || *c == 0)
-        return PA_MESSAGE_PARAMS_LIST_END;
-
-    element_count = 0;
-    open_braces = 0;
-    found_element = false;
-    found_backslash = false;
-    s = c;
-
-    /* Count elements in list */
-    while (*s != 0) {
-
-        /* Skip escaped curly braces. */
-        if (*s == '\\' && !found_backslash) {
-            found_backslash = true;
-            s++;
-            continue;
-        }
-
-        if (*s == '{' && !found_backslash) {
-            found_element = true;
-            open_braces++;
-        }
-        if (*s == '}' && !found_backslash)
-            open_braces--;
-
-        /* unexpected closing brace, parse error */
-        if (open_braces < 0)
-            return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
-        if (open_braces == 0 && found_element) {
-            element_count++;
-            found_element = false;
-        }
-
-        found_backslash = false;
-        s++;
-    }
-
-    /* missing closing brace, parse error */
-    if (open_braces > 0)
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
-    return element_count;
-}
-
-/* Split the specified string into elements. An element is defined as
- * a sub-string between curly braces. The function is needed to parse
- * the parameters of messages. Each time it is called it returns the
- * position of the current element in result and the state pointer is
- * advanced to the next list element. On return, the parameter
- * *is_unpacked indicates if the string is plain text or contains a
- * sub-list. is_unpacked may be NULL.
- *
- * The variable state points to, should be initialized to NULL before
- * the first call. The function returns 1 on success, 0 if end of string
- * is encountered and -1 on parse error.
- *
- * result is set to NULL on end of string or parse error. */
-static int split_list(char *c, char **result, bool *is_unpacked, void **state) {
-    char *current = *state ? *state : c;
-    uint32_t open_braces;
-    bool found_backslash = false;
-
-    pa_assert(result);
-
-    *result = NULL;
-
-    /* Empty or no string */
-    if (!current || *current == 0)
-        return PA_MESSAGE_PARAMS_LIST_END;
-
-    /* Find opening brace */
-    while (*current != 0) {
-
-        /* Skip escaped curly braces. */
-        if (*current == '\\' && !found_backslash) {
-            found_backslash = true;
-            current++;
-            continue;
-        }
-
-        if (*current == '{' && !found_backslash)
-            break;
-
-        /* unexpected closing brace, parse error */
-        if (*current == '}' && !found_backslash)
-            return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
-        found_backslash = false;
-        current++;
-    }
-
-    /* No opening brace found, end of string */
-    if (*current == 0)
-        return PA_MESSAGE_PARAMS_LIST_END;
-
-    if (is_unpacked)
-        *is_unpacked = true;
-    *result = current + 1;
-    found_backslash = false;
-    open_braces = 1;
-
-    while (open_braces != 0 && *current != 0) {
-        current++;
-
-        /* Skip escaped curly braces. */
-        if (*current == '\\' && !found_backslash) {
-            found_backslash = true;
-            continue;
-        }
-
-        if (*current == '{' && !found_backslash) {
-            open_braces++;
-            if (is_unpacked)
-                *is_unpacked = false;
-        }
-        if (*current == '}' && !found_backslash)
-            open_braces--;
-
-        found_backslash = false;
-    }
-
-    /* Parse error, closing brace missing */
-    if (open_braces != 0) {
-        *result = NULL;
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-    }
-
-    /* Replace } with 0 */
-    *current = 0;
-
-    *state = current + 1;
-
-    return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Read functions */
-
-/* Read a string from the parameter list. The state pointer is
- * advanced to the next element of the list. Returns a pointer
- * to a sub-string within c. Escape characters will be removed
- * from the string. The result must not be freed. */
-int pa_message_params_read_string(char *c, const char **result, void **state) {
-    char *start_pos;
-    char *value = NULL;
-    int r;
-    bool is_unpacked = true;
-
-    pa_assert(result);
-
-    if ((r = split_list(c, &start_pos, &is_unpacked, state)) == PA_MESSAGE_PARAMS_OK)
-        value = start_pos;
-
-    /* Check if we got a plain string not containing further lists */
-    if (!is_unpacked) {
-        /* Parse error */
-        r = PA_MESSAGE_PARAMS_PARSE_ERROR;
-        value = NULL;
-    }
-
-    if (value)
-        *result = pa_unescape(value);
-
-    return r;
-}
-
-/* A wrapper for split_list() to distinguish between reading pure
- * string data and raw data which may contain further lists. */
-int pa_message_params_read_raw(char *c, char **result, void **state) {
-    return split_list(c, result, NULL, state);
-}
-
-/* Read a double from the parameter list. The state pointer is
- * advanced to the next element of the list. */
-int pa_message_params_read_double(char *c, double *result, void **state) {
-    char *start_pos, *end_pos, *s;
-    int err;
-    struct lconv *locale;
-    double value;
-    bool is_unpacked = true;
-
-    pa_assert(result);
-
-    if ((err = split_list(c, &start_pos, &is_unpacked, state)) != PA_MESSAGE_PARAMS_OK)
-        return err;
-
-    /* Empty element */
-    if (!*start_pos)
-        return PA_MESSAGE_PARAMS_IS_NULL;
-
-    /* Check if we got a plain string not containing further lists */
-    if (!is_unpacked)
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
-    /* Get decimal separator for current locale */
-    locale = localeconv();
-
-    /* Replace decimal point with the correct character for the
-     * current locale. This assumes that no thousand separator
-     * is used. */
-    for (s = start_pos; *s; s++) {
-        if (*s == '.' || *s == ',')
-            *s = *locale->decimal_point;
-     }
-
-    /* Convert to double */
-    errno = 0;
-    value = strtod(start_pos, &end_pos);
-
-    /* Conversion error or string contains invalid characters. If the
-     * whole string was used for conversion, end_pos should point to
-     * the end of the string. */
-    if (errno != 0 || *end_pos != 0 || end_pos == start_pos)
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
-    *result = value;
-    return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Read an integer from the parameter list. The state pointer is
- * advanced to the next element of the list. */
-int pa_message_params_read_int64(char *c, int64_t *result, void **state) {
-    char *start_pos;
-    int err;
-    int64_t value;
-    bool is_unpacked = true;
-
-    pa_assert(result);
-
-    if ((err = split_list(c, &start_pos, &is_unpacked, state)) != PA_MESSAGE_PARAMS_OK)
-        return err;
-
-    /* Empty element */
-    if (!*start_pos)
-        return PA_MESSAGE_PARAMS_IS_NULL;
-
-    /* Check if we got a plain string not containing further lists */
-    if (!is_unpacked)
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
-    /* Convert to int64 */
-    if (pa_atoi64(start_pos, &value) < 0)
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
-    *result = value;
-    return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Read an unsigned integer from the parameter list. The state pointer is
- * advanced to the next element of the list. */
-int pa_message_params_read_uint64(char *c, uint64_t *result, void **state) {
-    char *start_pos;
-    int err;
-    uint64_t value;
-    bool is_unpacked = true;
-
-    pa_assert(result);
-
-    if ((err = split_list(c, &start_pos, &is_unpacked, state)) != PA_MESSAGE_PARAMS_OK)
-        return err;
-
-    /* Empty element */
-    if (!*start_pos)
-        return PA_MESSAGE_PARAMS_IS_NULL;
-
-    /* Check if we got a plain string not containing further lists */
-    if (!is_unpacked)
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
-    /* Convert to int64 */
-    if (pa_atou64(start_pos, &value) < 0)
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
-    *result = value;
-    return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Read a boolean from the parameter list. The state pointer is
- * advanced to the next element of the list. */
-int pa_message_params_read_bool(char *c, bool *result, void **state) {
-    int err;
-    uint64_t value;
-
-    pa_assert(result);
-
-    if ((err = pa_message_params_read_uint64(c, &value, state)) != PA_MESSAGE_PARAMS_OK)
-        return err;
-
-    *result = false;
-    if (value)
-        *result = true;
-
-    return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Converts a parameter list to a string array. */
-int pa_message_params_read_string_array(char *c, const char ***results, int *length, void **state) {
-    void *state1 = NULL;
-    int element_count, i;
-    int err;
-    const char **values;
-    char *start_pos;
-
-    pa_assert(results);
-    pa_assert(length);
-
-    if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
-        return err;
-
-    /* Count elements, return if no element was found or parse error. */
-    element_count = count_elements(start_pos);
-    if (element_count < 0) {
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-    } else if (element_count == 0) {
-        *length = 0;
-        return PA_MESSAGE_PARAMS_OK;
-    }
-
-    /* Allocate array */
-    values = pa_xmalloc0(element_count * sizeof(char *));
-
-    for (i = 0; (err = pa_message_params_read_string(start_pos, &(values[i]), &state1)) > 0; i++)
-        ;
-
-    if (err < 0) {
-        pa_xfree(values);
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-    }
-
-    *results = values;
-    *length = element_count;
-
-    return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Converts a parameter list to a double array. */
-int pa_message_params_read_double_array(char *c, double **results, int *length, void **state) {
-    double  *values;
-    void *state1 = NULL;
-    int element_count, i;
-    int err;
-    char *start_pos;
-
-    pa_assert(results);
-    pa_assert(length);
-
-    if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
-        return err;
-
-    /* Count elements, return if no element was found or parse error. */
-    element_count = count_elements(start_pos);
-    if (element_count < 0) {
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-    } else if (element_count == 0) {
-        *length = 0;
-        return PA_MESSAGE_PARAMS_OK;
-    }
-
-    /* Allocate array */
-    values = pa_xmalloc0(element_count * sizeof(double));
-
-    for (i = 0; (err = pa_message_params_read_double(start_pos, &(values[i]), &state1)) > 0; i++)
-        ;
-
-    if (err < 0) {
-        pa_xfree(values);
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-    }
-
-    *results = values;
-    *length = element_count;
-
-    return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Converts a parameter list to an int64 array. */
-int pa_message_params_read_int64_array(char *c, int64_t **results, int *length, void **state) {
-    int64_t  *values;
-    void *state1 = NULL;
-    int element_count, i;
-    int err;
-    char *start_pos;
-
-    pa_assert(results);
-    pa_assert(length);
-
-    if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
-        return err;
-
-    /* Count elements, return if no element was found or parse error. */
-    element_count = count_elements(start_pos);
-    if (element_count < 0) {
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-    } else if (element_count == 0) {
-        *length = 0;
-        return PA_MESSAGE_PARAMS_OK;
-    }
-
-    /* Allocate array */
-    values = pa_xmalloc0(element_count * sizeof(int64_t));
-
-    for (i = 0; (err = pa_message_params_read_int64(start_pos, &(values[i]), &state1)) > 0; i++)
-        ;
-
-    if (err < 0) {
-        pa_xfree(values);
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-    }
-
-    *results = values;
-    *length = element_count;
-
-    return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Converts a parameter list to an uint64 array. */
-int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length, void **state) {
-    uint64_t  *values;
-    void *state1 = NULL;
-    int element_count, i;
-    int err;
-    char *start_pos;
-
-    pa_assert(results);
-    pa_assert(length);
-
-    if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
-        return err;
-
-    /* Count elements, return if no element was found or parse error. */
-    element_count = count_elements(start_pos);
-    if (element_count < 0) {
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-    } else if (element_count == 0) {
-        *length = 0;
-        return PA_MESSAGE_PARAMS_OK;
-    }
-
-    /* Allocate array */
-    values = pa_xmalloc0(element_count * sizeof(uint64_t));
-
-    for (i = 0; (err = pa_message_params_read_uint64(start_pos, &(values[i]), &state1)) > 0; i++)
-        ;
-
-    if (err < 0) {
-        pa_xfree(values);
-        return PA_MESSAGE_PARAMS_PARSE_ERROR;
-    }
-
-    *results = values;
-    *length = element_count;
-
-    return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Write functions. The functions are wrapper functions around pa_strbuf,
- * so that the client does not need to use pa_strbuf directly. */
-
-/* Creates a new pa_message_param structure */
-pa_message_params *pa_message_params_new(void) {
-    pa_message_params *params;
-
-    params = pa_xnew(pa_message_params, 1);
-    params->buffer = pa_strbuf_new();
-
-    return params;
-}
-
-/* Frees a pa_message_params structure */
-void pa_message_params_free(pa_message_params *params) {
-    pa_assert(params);
-
-    pa_strbuf_free(params->buffer);
-    pa_xfree(params);
-}
-
-/* Converts a pa_message_param structure to string and frees the structure.
- * The returned string needs to be freed with pa_xree(). */
-char *pa_message_params_to_string_free(pa_message_params *params) {
-    char *result;
-
-    pa_assert(params);
-
-    result = pa_strbuf_to_string_free(params->buffer);
-
-    pa_xfree(params);
-    return result;
-}
-
-/* Writes an opening curly brace */
-void pa_message_params_begin_list(pa_message_params *params) {
-
-    pa_assert(params);
-
-    pa_strbuf_putc(params->buffer, '{');
-}
-
-/* Writes a closing curly brace */
-void pa_message_params_end_list(pa_message_params *params) {
-
-    pa_assert(params);
-
-    pa_strbuf_putc(params->buffer, '}');
-}
-
-/* Writes a string to a message_params structure, adding curly braces
- * around the string and escaping curly braces within the string. */
-void pa_message_params_write_string(pa_message_params *params, const char *value) {
-    char *output;
-
-    pa_assert(params);
-
-    /* Null value is written as empty element */
-    if (!value)
-        value = "";
-
-    output = pa_escape(value, "{}");
-    pa_strbuf_printf(params->buffer, "{%s}", output);
-
-    pa_xfree(output);
-}
-
-/* Writes a raw string to a message_params structure, adding curly braces
- * around the string if add_braces is true. This function can be used to
- * write parts of a string or whole parameter lists that have been prepared
- * elsewhere (for example an array). */
-void pa_message_params_write_raw(pa_message_params *params, const char *value, bool add_braces) {
-    pa_assert(params);
-
-    /* Null value is written as empty element if add_braces is true.
-     * Otherwise nothing is written. */
-    if (!value)
-        value = "";
-
-    if (add_braces)
-        pa_strbuf_printf(params->buffer, "{%s}", value);
-    else
-        pa_strbuf_puts(params->buffer, value);
-}
-
-/* Writes a double to a message_params structure, adding curly braces.
- * precision gives the number of significant digits, not digits after
- * the decimal point. */
-void pa_message_params_write_double(pa_message_params *params, double value, int precision) {
-    char *buf, *s;
-
-    pa_assert(params);
-
-    /* We do not care about locale because we do not know which locale is
-     * used on the server side. If the decimal separator is a comma, we
-     * replace it with a dot to achieve consistent output on all locales. */
-    buf = pa_sprintf_malloc("{%.*g}",  precision, value);
-    for (s = buf; *s; s++) {
-        if (*s == ',') {
-            *s = '.';
-            break;
-        }
-     }
-
-    pa_strbuf_puts(params->buffer, buf);
-
-    pa_xfree(buf);
-}
-
-/* Writes an integer to a message_param structure, adding curly braces. */
-void pa_message_params_write_int64(pa_message_params *params, int64_t value) {
-
-    pa_assert(params);
-
-    pa_strbuf_printf(params->buffer, "{%lli}", (long long)value);
-}
-
-/* Writes an unsigned integer to a message_params structure, adding curly braces. */
-void pa_message_params_write_uint64(pa_message_params *params, uint64_t value) {
-
-    pa_assert(params);
-
-    pa_strbuf_printf(params->buffer, "{%llu}", (unsigned long long)value);
-}
-
-/* Writes a boolean to a message_params structure, adding curly braces. */
-void pa_message_params_write_bool(pa_message_params *params, bool value) {
-
-    pa_assert(params);
-
-    if (value)
-        pa_strbuf_puts(params->buffer, "{1}");
-    else
-        pa_strbuf_puts(params->buffer, "{0}");
-}


=====================================
src/pulse/message-params.h deleted
=====================================
@@ -1,157 +0,0 @@
-#ifndef foomessagehelperhfoo
-#define foomessagehelperhfoo
-
-/***
-  This file is part of PulseAudio.
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as
-  published by the Free Software Foundation; either version 2.1 of the
-  License, or (at your option) any later version.
-
-  PulseAudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stddef.h>
-#include <stdbool.h>
-#include <inttypes.h>
-
-#include <pulse/cdecl.h>
-#include <pulse/version.h>
-
-/** \file
- * Utility functions for reading and writing message parameters.
- * All read functions return a value from pa_message_params_error_code
- * and the read value in result (or *result for string functions).
- * The string read functions read_string() and read_raw() return a pointer
- * to a sub-string within the parameter list in *result, therefore the
- * string in *result must not be freed and is only valid within the
- * message handler callback function. If the string is needed outside
- * the callback, it must be copied using pa_xstrdup().
- * When a read function is called, the state pointer is advanced to the
- * next list element. The variable state points to should be initialized
- * to NULL before the first call.
- * All read functions except read_raw() preserve a default value passed
- * in result if the call fails. For the array functions, results must be
- * initialized prior to the call either to NULL or to an array with default
- * values. If the function succeeds, the default array will be freed and
- * the number of elements in the result array is returned.\n\n
- * Write functions operate on a pa_message_params structure which is a
- * wrapper for pa_strbuf. A parameter list or sub-list is started by a
- * call to begin_list() and ended by a call to end_list().
- * A pa_message_params structure must be converted to a string using
- * pa_message_params_to_string_free() before it can be passed to a
- * message handler. */
-
-PA_C_DECL_BEGIN
-
-/** Structure which holds a parameter list. Wrapper for pa_strbuf  \since 15.0 */
-typedef struct pa_message_params pa_message_params;
-
-/** Read function return values  \since 15.0 */
-enum pa_message_params_error_code {
-    /** No value (empty element) found for numeric or boolean value */
-    PA_MESSAGE_PARAMS_IS_NULL = -2,
-    /** Error encountered while parsing a value */
-    PA_MESSAGE_PARAMS_PARSE_ERROR = -1,
-    /** End of parameter list reached */
-    PA_MESSAGE_PARAMS_LIST_END = 0,
-    /** Parsing successful */
-    PA_MESSAGE_PARAMS_OK = 1,
-};
-
-/** @{ \name Read functions */
-
-/** Read a boolean from parameter list in c. \since 15.0 */
-int pa_message_params_read_bool(char *c, bool *result, void **state);
-
-/** Read a double from parameter list in c. \since 15.0 */
-int pa_message_params_read_double(char *c, double *result, void **state);
-
-/** Converts a parameter list to a double array. Empty elements in the parameter
- * list are treated as error. Returns allocated array in *results and array size in *length.
- * The returned array must be freed with pa_xfree(). \since 15.0 */
-int pa_message_params_read_double_array(char *c, double **results, int *length, void **state);
-
-/** Read an integer from parameter list in c. \since 15.0 */
-int pa_message_params_read_int64(char *c, int64_t *result, void **state);
-
-/** Converts a parameter list to an int64 array. Empty elements in the parameter
- * list are treated as error. Returns allocated array in *results and array size in *length.
- * The returned array must be freed with pa_xfree(). \since 15.0 */
-int pa_message_params_read_int64_array(char *c, int64_t **results, int *length, void **state);
-
-/** Read raw data from parameter list in c. Used to split a message parameter
- * string into list elements. The string returned in *result must not be freed.  \since 15.0 */
-int pa_message_params_read_raw(char *c, char **result, void **state);
-
-/** Read a string from a parameter list in c. Escaped curly braces and backslashes
- * will be unescaped. \since 15.0 */
-int pa_message_params_read_string(char *c, const char **result, void **state);
-
-/** Convert a parameter list to a string array. Escaping is removed from
- * the strings. Returns allocated array of pointers to sub-strings within c in
- * *results and stores array size in *length. The returned array must be
- * freed with pa_xfree(), but not the strings within the array. \since 15.0 */
-int pa_message_params_read_string_array(char *c, const char ***results, int *length, void **state);
-
-/** Read an unsigned integer from parameter list in c. \since 15.0 */
-int pa_message_params_read_uint64(char *c, uint64_t *result, void **state);
-
-/** Converts a parameter list to an uint64 array. Empty elements in the parameter
- * list are treated as error. Returns allocated array in *results and array size in *length.
- * The returned array must be freed with pa_xfree(). \since 15.0 */
-int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length, void **state);
-
-/** @} */
-
-/** @{ \name Write functions */
-
-/** Create a new pa_message_params structure.  \since 15.0 */
-pa_message_params *pa_message_params_new(void);
-
-/** Free a pa_message_params structure.  \since 15.0 */
-void pa_message_params_free(pa_message_params *params);
-
-/** Convert pa_message_params to string, free pa_message_params structure.  \since 15.0 */
-char *pa_message_params_to_string_free(pa_message_params *params);
-
-/** Start a list by writing an opening brace.  \since 15.0 */
-void pa_message_params_begin_list(pa_message_params *params);
-
-/** End a list by writing a closing brace.  \since 15.0 */
-void pa_message_params_end_list(pa_message_params *params);
-
-/** Append a boolean to parameter list. \since 15.0 */
-void pa_message_params_write_bool(pa_message_params *params, bool value);
-
-/** Append a double to parameter list. Precision gives the number of
- * significant digits. The decimal separator will always be written as
- * dot, regardless which locale is used. \since 15.0 */
-void pa_message_params_write_double(pa_message_params *params, double value, int precision);
-
-/** Append an integer to parameter list. \since 15.0 */
-void pa_message_params_write_int64(pa_message_params *params, int64_t value);
-
-/** Append string to parameter list. Curly braces and backslashes will be escaped.  \since 15.0 */
-void pa_message_params_write_string(pa_message_params *params, const char *value);
-
-/** Append raw string to parameter list. Used to write incomplete strings
- * or complete parameter lists (for example arrays). Adds curly braces around
- * the string if add_braces is true.  \since 15.0 */
-void pa_message_params_write_raw(pa_message_params *params, const char *value, bool add_braces);
-
-/** Append an unsigned integer to parameter list. \since 15.0 */
-void pa_message_params_write_uint64(pa_message_params *params, uint64_t value);
-
-/** @} */
-
-PA_C_DECL_END
-
-#endif


=====================================
src/pulsecore/core.c
=====================================
@@ -29,7 +29,6 @@
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
 #include <pulse/xmalloc.h>
-#include <pulse/message-params.h>
 
 #include <pulsecore/module.h>
 #include <pulsecore/core-rtclock.h>
@@ -66,29 +65,27 @@ static void core_free(pa_object *o);
 
 /* Returns a list of handlers. */
 static char *message_handler_list(pa_core *c) {
-    pa_message_params *param;
+    pa_json_encoder *encoder;
     void *state = NULL;
     struct pa_message_handler *handler;
 
-    param = pa_message_params_new();
+    encoder = pa_json_encoder_new();
 
-    pa_message_params_begin_list(param);
+    pa_json_encoder_begin_element_array(encoder);
     PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
-        pa_message_params_begin_list(param);
+        pa_json_encoder_begin_element_object(encoder);
 
-        /* object_path cannot contain characters that need escaping, therefore
-         * pa_message_params_write_raw() can safely be used here. */
-        pa_message_params_write_raw(param, handler->object_path, true);
-        pa_message_params_write_string(param, handler->description);
+        pa_json_encoder_add_member_string(encoder, "name", handler->object_path);
+        pa_json_encoder_add_member_string(encoder, "description", handler->description);
 
-        pa_message_params_end_list(param);
+        pa_json_encoder_end_object(encoder);
     }
-    pa_message_params_end_list(param);
+    pa_json_encoder_end_array(encoder);
 
-    return pa_message_params_to_string_free(param);
+    return pa_json_encoder_to_string_free(encoder);
 }
 
-static int core_message_handler(const char *object_path, const char *message, char *message_parameters, char **response, void *userdata) {
+static int core_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
     pa_core *c;
 
     pa_assert(c = (pa_core *) userdata);


=====================================
src/pulsecore/message-handler.c
=====================================
@@ -105,7 +105,8 @@ void pa_message_handler_unregister(pa_core *c, const char *object_path) {
 int pa_message_handler_send_message(pa_core *c, const char *object_path, const char *message, const char *message_parameters, char **response) {
     struct pa_message_handler *handler;
     int ret;
-    char *parameter_copy, *path_copy;
+    char *path_copy;
+    pa_json_object *parameters = NULL;
 
     pa_assert(c);
     pa_assert(object_path);
@@ -125,14 +126,21 @@ int pa_message_handler_send_message(pa_core *c, const char *object_path, const c
         return -PA_ERR_NOENTITY;
     }
 
-    parameter_copy = pa_xstrdup(message_parameters);
+    pa_xfree(path_copy);
+
+    if (message_parameters) {
+        parameters = pa_json_parse(message_parameters);
+
+        if (!parameters)
+            return -PA_ERR_INVALID;
+    }
 
     /* The handler is expected to return an error code and may also
        return an error string in response */
-    ret = handler->callback(handler->object_path, message, parameter_copy, response, handler->userdata);
+    ret = handler->callback(handler->object_path, message, parameters, response, handler->userdata);
 
-    pa_xfree(parameter_copy);
-    pa_xfree(path_copy);
+    if (parameters)
+        pa_json_object_free(parameters);
     return ret;
 }
 


=====================================
src/pulsecore/message-handler.h
=====================================
@@ -19,6 +19,7 @@
 ***/
 
 #include <pulsecore/core.h>
+#include <pulse/json.h>
 
 /* Message handler types and functions */
 
@@ -26,7 +27,7 @@
 typedef int (*pa_message_handler_cb_t)(
         const char *object_path,
         const char *message,
-        char *message_parameters,
+        const pa_json_object *parameters,
         char **response,
         void *userdata);
 


=====================================
src/tests/json-test.c
=====================================
@@ -24,6 +24,7 @@
 #include <check.h>
 
 #include <pulse/json.h>
+#include <pulse/xmalloc.h>
 #include <pulsecore/core-util.h>
 
 START_TEST (string_test) {
@@ -50,11 +51,54 @@ START_TEST (string_test) {
 }
 END_TEST
 
+START_TEST (encoder_string_test) {
+    const char *test_strings[] = {
+        "", "test", "test123", "123", "newline\n", "  spaces ",
+        "lots of spaces", "esc\nape", "escape a \" quote",
+    };
+
+    pa_json_object *o;
+    unsigned int i;
+    pa_json_encoder *encoder;
+    const pa_json_object *v;
+    char *received;
+
+    encoder = pa_json_encoder_new();
+
+    pa_json_encoder_begin_element_array(encoder);
+
+    for (i = 0; i < PA_ELEMENTSOF(test_strings); i++) {
+        pa_json_encoder_add_element_string(encoder, test_strings[i]);
+    }
+
+    pa_json_encoder_end_array(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(o) == PA_ELEMENTSOF(test_strings));
+
+    for (i = 0; i < PA_ELEMENTSOF(test_strings); i++) {
+        v = pa_json_object_get_array_member(o, i);
+
+        fail_unless(v != NULL);
+        fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+        fail_unless(pa_streq(pa_json_object_get_string(v), test_strings[i]));
+    }
+
+    pa_json_object_free(o);
+}
+END_TEST
+
 START_TEST(int_test) {
     pa_json_object *o;
     unsigned int i;
     const char *ints_parse[] = { "1", "-1", "1234", "0" };
-    const int ints_compare[] = { 1, -1, 1234, 0 };
+    const int64_t ints_compare[] = { 1, -1, 1234, 0 };
+    char *ulong_max_str;
 
     for (i = 0; i < PA_ELEMENTSOF(ints_parse); i++) {
         o = pa_json_parse(ints_parse[i]);
@@ -65,12 +109,58 @@ START_TEST(int_test) {
 
         pa_json_object_free(o);
     }
+
+    /* test that parser would fail on integer overflow */
+    ulong_max_str = pa_sprintf_malloc("%"PRIu64, ULONG_MAX);
+    o = pa_json_parse(ulong_max_str);
+    fail_unless(o == NULL);
+    pa_xfree(ulong_max_str);
+}
+END_TEST
+
+START_TEST(encoder_int_test) {
+    const int64_t test_ints[] = { 1, -1, 1234, 0, LONG_MIN, LONG_MAX };
+
+    pa_json_object *o;
+    unsigned int i;
+    pa_json_encoder *encoder;
+    const pa_json_object *v;
+    char *received;
+
+    encoder = pa_json_encoder_new();
+
+    pa_json_encoder_begin_element_array(encoder);
+
+    for (i = 0; i < PA_ELEMENTSOF(test_ints); i++) {
+        pa_json_encoder_add_element_int(encoder, test_ints[i]);
+    }
+
+    pa_json_encoder_end_array(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(o) == PA_ELEMENTSOF(test_ints));
+
+    for (i = 0; i < PA_ELEMENTSOF(test_ints); i++) {
+        v = pa_json_object_get_array_member(o, i);
+
+        fail_unless(v != NULL);
+        fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_INT);
+        fail_unless(pa_json_object_get_int(v) == test_ints[i]);
+    }
+
+    pa_json_object_free(o);
 }
 END_TEST
 
 START_TEST(double_test) {
     pa_json_object *o;
     unsigned int i;
+    char *very_large_double_str;
     const char *doubles_parse[] = {
         "1.0", "-1.1", "1234e2", "1234e0", "0.1234", "-0.1234", "1234e-1", "1234.5e-1", "1234.5e+2",
     };
@@ -87,6 +177,52 @@ START_TEST(double_test) {
 
         pa_json_object_free(o);
     }
+
+    /* test that parser would fail on double exponent overflow */
+    very_large_double_str = pa_sprintf_malloc("%"PRIu64"e%"PRIu64, ULONG_MAX, ULONG_MAX);
+    o = pa_json_parse(very_large_double_str);
+    fail_unless(o == NULL);
+    pa_xfree(very_large_double_str);
+}
+END_TEST
+
+START_TEST(encoder_double_test) {
+    const double test_doubles[] = {
+        1.0, -1.1, 123400.0, 1234.0, 0.1234, -0.1234, 123.4, 123.45, 123450.0,
+    };
+    pa_json_object *o;
+    unsigned int i;
+    pa_json_encoder *encoder;
+    const pa_json_object *v;
+    char *received;
+
+    encoder = pa_json_encoder_new();
+
+    pa_json_encoder_begin_element_array(encoder);
+
+    for (i = 0; i < PA_ELEMENTSOF(test_doubles); i++) {
+        pa_json_encoder_add_element_double(encoder, test_doubles[i], 6);
+    }
+
+    pa_json_encoder_end_array(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(o) == PA_ELEMENTSOF(test_doubles));
+
+    for (i = 0; i < PA_ELEMENTSOF(test_doubles); i++) {
+        v = pa_json_object_get_array_member(o, i);
+
+        fail_unless(v != NULL);
+        fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_DOUBLE);
+        fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(v), test_doubles[i]));
+    }
+
+    pa_json_object_free(o);
 }
 END_TEST
 
@@ -102,6 +238,25 @@ START_TEST(null_test) {
 }
 END_TEST
 
+START_TEST(encoder_null_test) {
+    pa_json_object *o;
+    pa_json_encoder *encoder;
+    char *received;
+
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_add_element_null(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_NULL);
+
+    pa_json_object_free(o);
+}
+END_TEST
+
 START_TEST(bool_test) {
     pa_json_object *o;
 
@@ -123,6 +278,46 @@ START_TEST(bool_test) {
 }
 END_TEST
 
+START_TEST(encoder_bool_test) {
+    const bool test_bools[] = {
+        true, false
+    };
+    pa_json_object *o;
+    unsigned int i;
+    pa_json_encoder *encoder;
+    const pa_json_object *v;
+    char *received;
+
+    encoder = pa_json_encoder_new();
+
+    pa_json_encoder_begin_element_array(encoder);
+
+    for (i = 0; i < PA_ELEMENTSOF(test_bools); i++) {
+        pa_json_encoder_add_element_bool(encoder, test_bools[i]);
+    }
+
+    pa_json_encoder_end_array(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(o) == PA_ELEMENTSOF(test_bools));
+
+    for (i = 0; i < PA_ELEMENTSOF(test_bools); i++) {
+        v = pa_json_object_get_array_member(o, i);
+
+        fail_unless(v != NULL);
+        fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_BOOL);
+        fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_bool(v), test_bools[i]));
+    }
+
+    pa_json_object_free(o);
+}
+END_TEST
+
 START_TEST(object_test) {
     pa_json_object *o;
     const pa_json_object *v;
@@ -178,6 +373,165 @@ START_TEST(object_test) {
 }
 END_TEST
 
+START_TEST(object_member_iterator_test) {
+    pa_json_object *o;
+    const pa_json_object *v;
+    const char *k;
+    void *state;
+    size_t i;
+
+    struct {
+        bool visited;
+        const char *key;
+        pa_json_type type;
+        union {
+            const char *str;
+            int64_t n;
+        } value;
+    } expected_entries[] = {
+            { .key = "name", .type = PA_JSON_TYPE_STRING, .value.str = "sample 1" },
+            { .key = "number", .type = PA_JSON_TYPE_INT, .value.n = 42 },
+    };
+
+    o = pa_json_parse(" { \"name\" : \"sample 1\", \"number\": 42 } ");
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    PA_HASHMAP_FOREACH_KV(k, v, pa_json_object_get_object_member_hashmap(o), state) {
+        fail_unless(k != NULL);
+        fail_unless(v != NULL);
+        for (i = 0; i < PA_ELEMENTSOF(expected_entries); ++i) {
+            if (pa_streq(expected_entries[i].key, k)) {
+                fail_unless(!expected_entries[i].visited);
+                fail_unless(expected_entries[i].type == pa_json_object_get_type(v));
+                switch (expected_entries[i].type) {
+                    case PA_JSON_TYPE_STRING:
+                        fail_unless(pa_streq(expected_entries[i].value.str, pa_json_object_get_string(v)));
+                        break;
+                    case PA_JSON_TYPE_INT:
+                        fail_unless(expected_entries[i].value.n == pa_json_object_get_int(v));
+                        break;
+                    default:
+                        /* unreachable */
+                        fail_unless(false);
+                        break;
+                }
+                expected_entries[i].visited = true;
+            }
+        }
+    }
+
+    pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(encoder_object_test) {
+    pa_json_object *o;
+    const pa_json_object *v;
+    pa_json_encoder *encoder;
+    char *received;
+
+    /* { "name" : "A Person" } */
+
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_begin_element_object(encoder);
+    pa_json_encoder_add_member_string(encoder, "name", "A Person");
+    pa_json_encoder_end_object(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "name");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+    fail_unless(pa_streq(pa_json_object_get_string(v), "A Person"));
+
+    pa_json_object_free(o);
+
+    /* { "age" : -45.3e-0 } */
+
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_begin_element_object(encoder);
+    pa_json_encoder_add_member_double(encoder, "age", -45.3e-0, 2);
+    pa_json_encoder_end_object(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "age");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_DOUBLE);
+    fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(v), -45.3));
+
+    pa_json_object_free(o);
+
+    /* {"person":true} */
+
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_begin_element_object(encoder);
+    pa_json_encoder_add_member_bool(encoder, "person", true);
+    pa_json_encoder_end_object(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "person");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_BOOL);
+    fail_unless(pa_json_object_get_bool(v) == true);
+
+    pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(encoder_member_object_test) {
+    pa_json_object *o;
+    const pa_json_object *v;
+    pa_json_encoder *encoder;
+    char *received;
+
+    /* { "parent": { "child": false } } */
+
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_begin_element_object(encoder);
+
+    pa_json_encoder_begin_member_object(encoder, "parent");
+    pa_json_encoder_add_member_bool(encoder, "child", false);
+    pa_json_encoder_end_object(encoder);
+
+    pa_json_encoder_end_object(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "parent");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_OBJECT);
+    v = pa_json_object_get_object_member(v, "child");
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_BOOL);
+    fail_unless(pa_json_object_get_bool(v) == false);
+
+    pa_json_object_free(o);
+}
+END_TEST
+
 START_TEST(array_test) {
     pa_json_object *o;
     const pa_json_object *v, *v2;
@@ -229,13 +583,241 @@ START_TEST(array_test) {
 }
 END_TEST
 
+START_TEST(encoder_element_array_test) {
+    pa_json_object *o;
+    const pa_json_object *v, *v2;
+
+    pa_json_encoder *encoder;
+    char *received;
+    pa_json_encoder *subobject;
+    char *subobject_string;
+
+    /* [  ] */
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_begin_element_array(encoder);
+    pa_json_encoder_end_array(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(o) == 0);
+
+    pa_json_object_free(o);
+
+    /* ["a member"] */
+
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_begin_element_array(encoder);
+    pa_json_encoder_add_element_string(encoder, "a member");
+    pa_json_encoder_end_array(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(o) == 1);
+
+    v = pa_json_object_get_array_member(o, 0);
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+    fail_unless(pa_streq(pa_json_object_get_string(v), "a member"));
+
+    pa_json_object_free(o);
+
+    /* [\"a member\", 1234.5, { \"another\": true } ] */
+
+    subobject = pa_json_encoder_new();
+    pa_json_encoder_begin_element_object(subobject);
+    pa_json_encoder_add_member_bool(subobject, "another", true);
+    pa_json_encoder_end_object(subobject);
+    subobject_string = pa_json_encoder_to_string_free(subobject);
+
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_begin_element_array(encoder);
+    pa_json_encoder_add_element_string(encoder, "a member");
+    pa_json_encoder_add_element_double(encoder, 1234.5, 1);
+    pa_json_encoder_add_element_raw_json(encoder, subobject_string);
+    pa_xfree(subobject_string);
+    pa_json_encoder_end_array(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(o) == 3);
+
+    v = pa_json_object_get_array_member(o, 0);
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+    fail_unless(pa_streq(pa_json_object_get_string(v), "a member"));
+    v = pa_json_object_get_array_member(o, 1);
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_DOUBLE);
+    fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(v), 1234.5));
+    v = pa_json_object_get_array_member(o, 2);
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_OBJECT);
+    v2 =pa_json_object_get_object_member(v, "another");
+    fail_unless(v2 != NULL);
+    fail_unless(pa_json_object_get_type(v2) == PA_JSON_TYPE_BOOL);
+    fail_unless(pa_json_object_get_bool(v2) == true);
+
+    pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(encoder_member_array_test) {
+    pa_json_object *o;
+    unsigned int i;
+    const pa_json_object *v;
+    const pa_json_object *e;
+    pa_json_encoder *encoder;
+    char *received;
+
+    const int64_t test_ints[] = { 1, -1, 1234, 0, LONG_MIN, LONG_MAX };
+
+    /* { "parameters": [ 1, -1, 1234, 0, -9223372036854775808, 9223372036854775807 ] } */
+
+
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_begin_element_object(encoder);
+
+    pa_json_encoder_begin_member_array(encoder, "parameters");
+    for (i = 0; i < PA_ELEMENTSOF(test_ints); i++) {
+        pa_json_encoder_add_element_int(encoder, test_ints[i]);
+    }
+    pa_json_encoder_end_array(encoder);
+
+    pa_json_encoder_end_object(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "parameters");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(v) == PA_ELEMENTSOF(test_ints));
+
+    for (i = 0; i < PA_ELEMENTSOF(test_ints); i++) {
+        e = pa_json_object_get_array_member(v, i);
+
+        fail_unless(e != NULL);
+        fail_unless(pa_json_object_get_type(e) == PA_JSON_TYPE_INT);
+        fail_unless(pa_json_object_get_int(e) == test_ints[i]);
+    }
+
+    pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(encoder_member_raw_json_test) {
+    pa_json_object *o;
+    const pa_json_object *v;
+    const pa_json_object *e;
+    pa_json_encoder *encoder;
+    char *received;
+    pa_json_encoder *subobject;
+    char *subobject_string;
+
+    /* { "parameters": [1, "a", 2.0] } */
+
+    subobject = pa_json_encoder_new();
+    pa_json_encoder_begin_element_array(subobject);
+    pa_json_encoder_add_element_int(subobject, 1);
+    pa_json_encoder_add_element_string(subobject, "a");
+    pa_json_encoder_add_element_double(subobject, 2.0, 6);
+    pa_json_encoder_end_array(subobject);
+    subobject_string = pa_json_encoder_to_string_free(subobject);
+
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_begin_element_object(encoder);
+
+    pa_json_encoder_add_member_raw_json(encoder, "parameters", subobject_string);
+    pa_xfree(subobject_string);
+
+    pa_json_encoder_end_object(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "parameters");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_ARRAY);
+    fail_unless(pa_json_object_get_array_length(v) == 3);
+    e = pa_json_object_get_array_member(v, 0);
+    fail_unless(e != NULL);
+    fail_unless(pa_json_object_get_type(e) == PA_JSON_TYPE_INT);
+    fail_unless(pa_json_object_get_int(e) == 1);
+    e = pa_json_object_get_array_member(v, 1);
+    fail_unless(e != NULL);
+    fail_unless(pa_json_object_get_type(e) == PA_JSON_TYPE_STRING);
+    fail_unless(pa_streq(pa_json_object_get_string(e), "a"));
+    e = pa_json_object_get_array_member(v, 2);
+    fail_unless(e != NULL);
+    fail_unless(pa_json_object_get_type(e) == PA_JSON_TYPE_DOUBLE);
+    fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(e), 2.0));
+
+    pa_json_object_free(o);
+
+    /* { "parent": { "child": false } } */
+
+    subobject = pa_json_encoder_new();
+    pa_json_encoder_begin_element_object(subobject);
+    pa_json_encoder_add_member_bool(subobject, "child", false);
+    pa_json_encoder_end_object(subobject);
+    subobject_string = pa_json_encoder_to_string_free(subobject);
+
+    encoder = pa_json_encoder_new();
+    pa_json_encoder_begin_element_object(encoder);
+
+    pa_json_encoder_add_member_raw_json(encoder, "parent", subobject_string);
+    pa_xfree(subobject_string);
+
+    pa_json_encoder_end_object(encoder);
+
+    received = pa_json_encoder_to_string_free(encoder);
+    o = pa_json_parse(received);
+    pa_xfree(received);
+
+    fail_unless(o != NULL);
+    fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+    v = pa_json_object_get_object_member(o, "parent");
+    fail_unless(v != NULL);
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_OBJECT);
+    v = pa_json_object_get_object_member(v, "child");
+    fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_BOOL);
+    fail_unless(pa_json_object_get_bool(v) == false);
+
+    pa_json_object_free(o);
+}
+END_TEST
+
 START_TEST(bad_test) {
     unsigned int i;
     const char *bad_parse[] = {
         "\"" /* Quote not closed */,
         "123456789012345678901234567890" /* Overflow */,
+#if 0   /* TODO: check rounding the value is OK */
         "0.123456789012345678901234567890" /* Overflow */,
+#endif
         "1e123456789012345678901234567890" /* Overflow */,
+        "1e-10000" /* Underflow */,
         "1e" /* Bad number string */,
         "1." /* Bad number string */,
         "1.e3" /* Bad number string */,
@@ -266,12 +848,23 @@ int main(int argc, char *argv[]) {
     s = suite_create("JSON");
     tc = tcase_create("json");
     tcase_add_test(tc, string_test);
+    tcase_add_test(tc, encoder_string_test);
     tcase_add_test(tc, int_test);
+    tcase_add_test(tc, encoder_int_test);
     tcase_add_test(tc, double_test);
+    tcase_add_test(tc, encoder_double_test);
     tcase_add_test(tc, null_test);
+    tcase_add_test(tc, encoder_null_test);
     tcase_add_test(tc, bool_test);
+    tcase_add_test(tc, encoder_bool_test);
     tcase_add_test(tc, object_test);
+    tcase_add_test(tc, encoder_member_object_test);
+    tcase_add_test(tc, object_member_iterator_test);
+    tcase_add_test(tc, encoder_object_test);
     tcase_add_test(tc, array_test);
+    tcase_add_test(tc, encoder_element_array_test);
+    tcase_add_test(tc, encoder_member_array_test);
+    tcase_add_test(tc, encoder_member_raw_json_test);
     tcase_add_test(tc, bad_test);
     suite_add_tcase(s, tc);
 


=====================================
src/utils/pactl.c
=====================================
@@ -35,8 +35,8 @@
 #include <sndfile.h>
 
 #include <pulse/pulseaudio.h>
-#include <pulse/message-params.h>
 #include <pulse/ext-device-restore.h>
+#include <pulse/json.h>
 
 #include <pulsecore/i18n.h>
 #include <pulsecore/macro.h>
@@ -897,10 +897,10 @@ static void send_message_callback(pa_context *c, int success, char *response, vo
 }
 
 static void list_handlers_callback(pa_context *c, int success, char *response, void *userdata) {
-    void *state = NULL;
-    char *handler_list;
-    char *handler_struct;
     int err;
+    pa_json_object *o;
+    int i;
+    const pa_json_object *v, *path, *description;
 
     if (!success) {
         pa_log(_("list-handlers message failed: %s"), pa_strerror(pa_context_errno(c)));
@@ -908,29 +908,45 @@ static void list_handlers_callback(pa_context *c, int success, char *response, v
         return;
     }
 
-    if (pa_message_params_read_raw(response, &handler_list, &state) <= 0) {
+    o = pa_json_parse(response);
+
+    if (!o) {
         pa_log(_("list-handlers message response could not be parsed correctly"));
+        pa_json_object_free(o);
+        quit(1);
+        return;
+    }
+
+    if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY) {
+        pa_log(_("list-handlers message response is not a JSON array"));
+        pa_json_object_free(o);
         quit(1);
         return;
     }
 
-    state = NULL;
-    while ((err = pa_message_params_read_raw(handler_list, &handler_struct, &state)) > 0) {
-        void *state2 = NULL;
-        const char *path;
-        const char *description;
+    err = 0;
 
-        if (pa_message_params_read_string(handler_struct, &path, &state2) <= 0) {
+    for (i = 0; i < pa_json_object_get_array_length(o); ++i) {
+        v = pa_json_object_get_array_member(o, i);
+        if (pa_json_object_get_type(v) != PA_JSON_TYPE_OBJECT) {
+            pa_log(_("list-handlers message response array element %d is not a JSON object"), i);
             err = -1;
             break;
         }
-        if (pa_message_params_read_string(handler_struct, &description, &state2) <= 0) {
+
+        path = pa_json_object_get_object_member(v, "name");
+        if (!path || pa_json_object_get_type(path) != PA_JSON_TYPE_STRING) {
+            err = -1;
+            break;
+        }
+        description = pa_json_object_get_object_member(v, "description");
+        if (!description || pa_json_object_get_type(description) != PA_JSON_TYPE_STRING) {
             err = -1;
             break;
         }
 
         if (short_list_format)
-            printf("%s\n", path);
+            printf("%s\n", pa_json_object_get_string(path));
         else {
             if (nl)
                 printf("\n");
@@ -938,17 +954,20 @@ static void list_handlers_callback(pa_context *c, int success, char *response, v
 
             printf("Message Handler %s\n"
                    "\tDescription: %s\n",
-                   path,
-                   description);
+                   pa_json_object_get_string(path),
+                   pa_json_object_get_string(description));
         }
     }
 
     if (err < 0) {
         pa_log(_("list-handlers message response could not be parsed correctly"));
+        pa_json_object_free(o);
         quit(1);
         return;
     }
 
+    pa_json_object_free(o);
+
     complete_action();
 }
 



View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/02027518afa3717724d1a545c74662aaf753ff8e...7ca50bab2f946237bbe6418bf9e7dbd01fada1bd

-- 
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/02027518afa3717724d1a545c74662aaf753ff8e...7ca50bab2f946237bbe6418bf9e7dbd01fada1bd
You're receiving this email because of your account on gitlab.freedesktop.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/pulseaudio-commits/attachments/20210327/cb90dfa4/attachment-0001.htm>


More information about the pulseaudio-commits mailing list