From b7ff40029a68ba33a18258cc18aa9c40fbff259a Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Mon, 18 May 2020 13:53:20 +0200
Subject: [PATCH] :boom: change serialization of binary values

---
 include/nlohmann/detail/output/serializer.hpp | 101 +++++++++----
 include/nlohmann/json.hpp                     |  21 ++-
 single_include/nlohmann/json.hpp              | 122 ++++++++++-----
 test/src/unit-cbor.cpp                        |   2 +-
 test/src/unit-regression.cpp                  |   3 +-
 test/src/unit-serialization.cpp               | 140 +++++++++++++-----
 6 files changed, 273 insertions(+), 116 deletions(-)

diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp
index dc963f53..e8f31fd8 100644
--- a/include/nlohmann/detail/output/serializer.hpp
+++ b/include/nlohmann/detail/output/serializer.hpp
@@ -84,19 +84,22 @@ class serializer
     - strings and object keys are escaped using `escape_string()`
     - integer numbers are converted implicitly via `operator<<`
     - floating-point numbers are converted to a string using `"%g"` format
-    - if specified to, binary values are output using the syntax `b[]`, otherwise an exception is thrown
+    - binary values are serialized as objects containing the subtype and the
+      byte array
 
     @param[in] val               value to serialize
     @param[in] pretty_print      whether the output shall be pretty-printed
+    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
+    in the output are escaped with `\uXXXX` sequences, and the result consists
+    of ASCII characters only.
     @param[in] indent_step       the indent level
     @param[in] current_indent    the current indent level (only used internally)
-    @param[in] serialize_binary  whether the output shall include non-standard binary output
     */
-    void dump(const BasicJsonType& val, const bool pretty_print,
+    void dump(const BasicJsonType& val,
+              const bool pretty_print,
               const bool ensure_ascii,
               const unsigned int indent_step,
-              const unsigned int current_indent = 0,
-              const bool serialize_binary = false)
+              const unsigned int current_indent = 0)
     {
         switch (val.m_type)
         {
@@ -127,7 +130,7 @@ class serializer
                         o->write_character('\"');
                         dump_escaped(i->first, ensure_ascii);
                         o->write_characters("\": ", 3);
-                        dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary);
+                        dump(i->second, true, ensure_ascii, indent_step, new_indent);
                         o->write_characters(",\n", 2);
                     }
 
@@ -138,7 +141,7 @@ class serializer
                     o->write_character('\"');
                     dump_escaped(i->first, ensure_ascii);
                     o->write_characters("\": ", 3);
-                    dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary);
+                    dump(i->second, true, ensure_ascii, indent_step, new_indent);
 
                     o->write_character('\n');
                     o->write_characters(indent_string.c_str(), current_indent);
@@ -155,7 +158,7 @@ class serializer
                         o->write_character('\"');
                         dump_escaped(i->first, ensure_ascii);
                         o->write_characters("\":", 2);
-                        dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary);
+                        dump(i->second, false, ensure_ascii, indent_step, current_indent);
                         o->write_character(',');
                     }
 
@@ -165,7 +168,7 @@ class serializer
                     o->write_character('\"');
                     dump_escaped(i->first, ensure_ascii);
                     o->write_characters("\":", 2);
-                    dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary);
+                    dump(i->second, false, ensure_ascii, indent_step, current_indent);
 
                     o->write_character('}');
                 }
@@ -197,14 +200,14 @@ class serializer
                             i != val.m_value.array->cend() - 1; ++i)
                     {
                         o->write_characters(indent_string.c_str(), new_indent);
-                        dump(*i, true, ensure_ascii, indent_step, new_indent, serialize_binary);
+                        dump(*i, true, ensure_ascii, indent_step, new_indent);
                         o->write_characters(",\n", 2);
                     }
 
                     // last element
                     assert(not val.m_value.array->empty());
                     o->write_characters(indent_string.c_str(), new_indent);
-                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent, serialize_binary);
+                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
 
                     o->write_character('\n');
                     o->write_characters(indent_string.c_str(), current_indent);
@@ -218,13 +221,13 @@ class serializer
                     for (auto i = val.m_value.array->cbegin();
                             i != val.m_value.array->cend() - 1; ++i)
                     {
-                        dump(*i, false, ensure_ascii, indent_step, current_indent, serialize_binary);
+                        dump(*i, false, ensure_ascii, indent_step, current_indent);
                         o->write_character(',');
                     }
 
                     // last element
                     assert(not val.m_value.array->empty());
-                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent, serialize_binary);
+                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
 
                     o->write_character(']');
                 }
@@ -242,27 +245,73 @@ class serializer
 
             case value_t::binary:
             {
-                if (not serialize_binary)
+                if (pretty_print)
                 {
-                    JSON_THROW(type_error::create(317, "cannot serialize binary data to text JSON"));
-                }
+                    o->write_characters("{\n", 2);
 
-                if (val.m_value.binary->empty())
-                {
-                    o->write_characters("b[]", 3);
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    o->write_characters(indent_string.c_str(), new_indent);
+
+                    o->write_characters("\"bytes\": [", 10);
+
+                    if (not val.m_value.binary->empty())
+                    {
+                        for (auto i = val.m_value.binary->cbegin();
+                                i != val.m_value.binary->cend() - 1; ++i)
+                        {
+                            dump_integer(*i);
+                            o->write_characters(", ", 2);
+                        }
+                        dump_integer(val.m_value.binary->back());
+                    }
+
+                    o->write_characters("],\n", 3);
+                    o->write_characters(indent_string.c_str(), new_indent);
+
+                    o->write_characters("\"subtype\": ", 11);
+                    if (val.m_value.binary->has_subtype())
+                    {
+                        dump_integer(val.m_value.binary->subtype());
+                    }
+                    else
+                    {
+                        o->write_characters("null", 4);
+                    }
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character('}');
                 }
                 else
                 {
-                    o->write_characters("b[", 2);
-                    for (auto i = val.m_value.binary->cbegin();
-                            i != val.m_value.binary->cend() - 1; ++i)
+                    o->write_characters("{\"bytes\":[", 10);
+
+                    if (not val.m_value.binary->empty())
                     {
-                        dump_integer(*i);
-                        o->write_character(',');
+                        for (auto i = val.m_value.binary->cbegin();
+                                i != val.m_value.binary->cend() - 1; ++i)
+                        {
+                            dump_integer(*i);
+                            o->write_character(',');
+                        }
+                        dump_integer(val.m_value.binary->back());
                     }
 
-                    dump_integer(val.m_value.binary->back());
-                    o->write_character(']');
+                    o->write_characters("],\"subtype\":", 12);
+                    if (val.m_value.binary->has_subtype())
+                    {
+                        dump_integer(val.m_value.binary->subtype());
+                        o->write_character('}');
+                    }
+                    else
+                    {
+                        o->write_characters("null}", 5);
+                    }
                 }
                 return;
             }
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index fc849a38..fc4d8da1 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -2234,16 +2234,15 @@ class basic_json
     possible values: `strict` (throws and exception in case a decoding error
     occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),
     and `ignore` (ignore invalid UTF-8 sequences during serialization).
-    @param[in] serialize_binary Whether or not to allow serialization of binary
-    types to JSON.  Because binary types are non-standard, this will produce
-    non-conformant JSON, and is disabled by default.  This flag is primarily
-    useful for debugging.  Will output the binary value as a list of 8-bit
-    numbers prefixed by "b" (e.g. "bindata" = b[3, 0, 42, 255]).
 
     @return string containing the serialization of the JSON value
 
     @throw type_error.316 if a string stored inside the JSON value is not
-                          UTF-8 encoded
+                          UTF-8 encoded and @a error_handler is set to strict
+
+    @note Binary values are serialized as object containing two keys:
+      - "bytes": an array of bytes as integers
+      - "subtype": the subtype as integer or "null" if the binary has no subtype
 
     @complexity Linear.
 
@@ -2258,24 +2257,24 @@ class basic_json
 
     @since version 1.0.0; indentation character @a indent_char, option
            @a ensure_ascii and exceptions added in version 3.0.0; error
-           handlers added in version 3.4.0.
+           handlers added in version 3.4.0; serialization of binary values added
+           in version 3.8.0.
     */
     string_t dump(const int indent = -1,
                   const char indent_char = ' ',
                   const bool ensure_ascii = false,
-                  const error_handler_t error_handler = error_handler_t::strict,
-                  const bool serialize_binary = false) const
+                  const error_handler_t error_handler = error_handler_t::strict) const
     {
         string_t result;
         serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);
 
         if (indent >= 0)
         {
-            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent), 0, serialize_binary);
+            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
         }
         else
         {
-            s.dump(*this, false, ensure_ascii, 0, 0, serialize_binary);
+            s.dump(*this, false, ensure_ascii, 0);
         }
 
         return result;
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 0033be23..c94336e2 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -14828,19 +14828,22 @@ class serializer
     - strings and object keys are escaped using `escape_string()`
     - integer numbers are converted implicitly via `operator<<`
     - floating-point numbers are converted to a string using `"%g"` format
-    - if specified to, binary values are output using the syntax `b[]`, otherwise an exception is thrown
+    - binary values are serialized as objects containing the subtype and the
+      byte array
 
     @param[in] val               value to serialize
     @param[in] pretty_print      whether the output shall be pretty-printed
+    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
+    in the output are escaped with `\uXXXX` sequences, and the result consists
+    of ASCII characters only.
     @param[in] indent_step       the indent level
     @param[in] current_indent    the current indent level (only used internally)
-    @param[in] serialize_binary  whether the output shall include non-standard binary output
     */
-    void dump(const BasicJsonType& val, const bool pretty_print,
+    void dump(const BasicJsonType& val,
+              const bool pretty_print,
               const bool ensure_ascii,
               const unsigned int indent_step,
-              const unsigned int current_indent = 0,
-              const bool serialize_binary = false)
+              const unsigned int current_indent = 0)
     {
         switch (val.m_type)
         {
@@ -14871,7 +14874,7 @@ class serializer
                         o->write_character('\"');
                         dump_escaped(i->first, ensure_ascii);
                         o->write_characters("\": ", 3);
-                        dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary);
+                        dump(i->second, true, ensure_ascii, indent_step, new_indent);
                         o->write_characters(",\n", 2);
                     }
 
@@ -14882,7 +14885,7 @@ class serializer
                     o->write_character('\"');
                     dump_escaped(i->first, ensure_ascii);
                     o->write_characters("\": ", 3);
-                    dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary);
+                    dump(i->second, true, ensure_ascii, indent_step, new_indent);
 
                     o->write_character('\n');
                     o->write_characters(indent_string.c_str(), current_indent);
@@ -14899,7 +14902,7 @@ class serializer
                         o->write_character('\"');
                         dump_escaped(i->first, ensure_ascii);
                         o->write_characters("\":", 2);
-                        dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary);
+                        dump(i->second, false, ensure_ascii, indent_step, current_indent);
                         o->write_character(',');
                     }
 
@@ -14909,7 +14912,7 @@ class serializer
                     o->write_character('\"');
                     dump_escaped(i->first, ensure_ascii);
                     o->write_characters("\":", 2);
-                    dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary);
+                    dump(i->second, false, ensure_ascii, indent_step, current_indent);
 
                     o->write_character('}');
                 }
@@ -14941,14 +14944,14 @@ class serializer
                             i != val.m_value.array->cend() - 1; ++i)
                     {
                         o->write_characters(indent_string.c_str(), new_indent);
-                        dump(*i, true, ensure_ascii, indent_step, new_indent, serialize_binary);
+                        dump(*i, true, ensure_ascii, indent_step, new_indent);
                         o->write_characters(",\n", 2);
                     }
 
                     // last element
                     assert(not val.m_value.array->empty());
                     o->write_characters(indent_string.c_str(), new_indent);
-                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent, serialize_binary);
+                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
 
                     o->write_character('\n');
                     o->write_characters(indent_string.c_str(), current_indent);
@@ -14962,13 +14965,13 @@ class serializer
                     for (auto i = val.m_value.array->cbegin();
                             i != val.m_value.array->cend() - 1; ++i)
                     {
-                        dump(*i, false, ensure_ascii, indent_step, current_indent, serialize_binary);
+                        dump(*i, false, ensure_ascii, indent_step, current_indent);
                         o->write_character(',');
                     }
 
                     // last element
                     assert(not val.m_value.array->empty());
-                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent, serialize_binary);
+                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
 
                     o->write_character(']');
                 }
@@ -14986,27 +14989,73 @@ class serializer
 
             case value_t::binary:
             {
-                if (not serialize_binary)
+                if (pretty_print)
                 {
-                    JSON_THROW(type_error::create(317, "cannot serialize binary data to text JSON"));
-                }
+                    o->write_characters("{\n", 2);
 
-                if (val.m_value.binary->empty())
-                {
-                    o->write_characters("b[]", 3);
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    o->write_characters(indent_string.c_str(), new_indent);
+
+                    o->write_characters("\"bytes\": [", 10);
+
+                    if (not val.m_value.binary->empty())
+                    {
+                        for (auto i = val.m_value.binary->cbegin();
+                                i != val.m_value.binary->cend() - 1; ++i)
+                        {
+                            dump_integer(*i);
+                            o->write_characters(", ", 2);
+                        }
+                        dump_integer(val.m_value.binary->back());
+                    }
+
+                    o->write_characters("],\n", 3);
+                    o->write_characters(indent_string.c_str(), new_indent);
+
+                    o->write_characters("\"subtype\": ", 11);
+                    if (val.m_value.binary->has_subtype())
+                    {
+                        dump_integer(val.m_value.binary->subtype());
+                    }
+                    else
+                    {
+                        o->write_characters("null", 4);
+                    }
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character('}');
                 }
                 else
                 {
-                    o->write_characters("b[", 2);
-                    for (auto i = val.m_value.binary->cbegin();
-                            i != val.m_value.binary->cend() - 1; ++i)
+                    o->write_characters("{\"bytes\":[", 10);
+
+                    if (not val.m_value.binary->empty())
                     {
-                        dump_integer(*i);
-                        o->write_character(',');
+                        for (auto i = val.m_value.binary->cbegin();
+                                i != val.m_value.binary->cend() - 1; ++i)
+                        {
+                            dump_integer(*i);
+                            o->write_character(',');
+                        }
+                        dump_integer(val.m_value.binary->back());
                     }
 
-                    dump_integer(val.m_value.binary->back());
-                    o->write_character(']');
+                    o->write_characters("],\"subtype\":", 12);
+                    if (val.m_value.binary->has_subtype())
+                    {
+                        dump_integer(val.m_value.binary->subtype());
+                        o->write_character('}');
+                    }
+                    else
+                    {
+                        o->write_characters("null}", 5);
+                    }
                 }
                 return;
             }
@@ -17963,16 +18012,15 @@ class basic_json
     possible values: `strict` (throws and exception in case a decoding error
     occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),
     and `ignore` (ignore invalid UTF-8 sequences during serialization).
-    @param[in] serialize_binary Whether or not to allow serialization of binary
-    types to JSON.  Because binary types are non-standard, this will produce
-    non-conformant JSON, and is disabled by default.  This flag is primarily
-    useful for debugging.  Will output the binary value as a list of 8-bit
-    numbers prefixed by "b" (e.g. "bindata" = b[3, 0, 42, 255]).
 
     @return string containing the serialization of the JSON value
 
     @throw type_error.316 if a string stored inside the JSON value is not
-                          UTF-8 encoded
+                          UTF-8 encoded and @a error_handler is set to strict
+
+    @note Binary values are serialized as object containing two keys:
+      - "bytes": an array of bytes as integers
+      - "subtype": the subtype as integer or "null" if the binary has no subtype
 
     @complexity Linear.
 
@@ -17987,24 +18035,24 @@ class basic_json
 
     @since version 1.0.0; indentation character @a indent_char, option
            @a ensure_ascii and exceptions added in version 3.0.0; error
-           handlers added in version 3.4.0.
+           handlers added in version 3.4.0; serialization of binary values added
+           in version 3.8.0.
     */
     string_t dump(const int indent = -1,
                   const char indent_char = ' ',
                   const bool ensure_ascii = false,
-                  const error_handler_t error_handler = error_handler_t::strict,
-                  const bool serialize_binary = false) const
+                  const error_handler_t error_handler = error_handler_t::strict) const
     {
         string_t result;
         serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);
 
         if (indent >= 0)
         {
-            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent), 0, serialize_binary);
+            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
         }
         else
         {
-            s.dump(*this, false, ensure_ascii, 0, 0, serialize_binary);
+            s.dump(*this, false, ensure_ascii, 0);
         }
 
         return result;
diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp
index 2a53bb52..d34ae789 100644
--- a/test/src/unit-cbor.cpp
+++ b/test/src/unit-cbor.cpp
@@ -1582,7 +1582,7 @@ TEST_CASE("CBOR")
                 auto j = json::from_cbor(input);
                 CHECK(j.is_binary());
                 auto k = json::binary_array({0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x99});
-                CAPTURE(j.dump(0, ' ', false, json::error_handler_t::strict, true))
+                CAPTURE(j.dump(0, ' ', false, json::error_handler_t::strict))
                 CHECK(j == k);
             }
 
diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp
index c191628a..373344d8 100644
--- a/test/src/unit-regression.cpp
+++ b/test/src/unit-regression.cpp
@@ -1919,8 +1919,7 @@ TEST_CASE("regression tests")
             j.dump(4,                              // Indent
                    ' ',                            // Indent char
                    false,                          // Ensure ascii
-                   json::error_handler_t::strict,  // Error
-                   true                            // Allow binary data
+                   json::error_handler_t::strict  // Error
                   )
         );
     }
diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp
index 2681ab53..d0ddc58f 100644
--- a/test/src/unit-serialization.cpp
+++ b/test/src/unit-serialization.cpp
@@ -209,52 +209,114 @@ TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint3
 
 TEST_CASE("dump with binary values")
 {
-    SECTION("serialize_binary = false")
-    {
-        auto binary = json::binary_array({1, 2, 3, 4});
-        auto binary_empty = json::binary_array({});
-        json object = {{"key", binary}};
-        json array = {"value", 1, binary};
+    auto binary = json::binary_array({1, 2, 3, 4});
+    auto binary_empty = json::binary_array({});
+    auto binary_with_subtype = json::binary_array({1, 2, 3, 4}, 128);
+    auto binary_empty_with_subtype = json::binary_array({}, 128);
 
-        CHECK_THROWS_AS(binary.dump(), json::type_error);
-        CHECK_THROWS_AS(binary_empty.dump(), json::type_error);
-        CHECK_THROWS_AS(object.dump(), json::type_error);
-        CHECK_THROWS_AS(array.dump(), json::type_error);
-        CHECK_THROWS_WITH(binary.dump(), "[json.exception.type_error.317] cannot serialize binary data to text JSON");
-        CHECK_THROWS_WITH(binary_empty.dump(), "[json.exception.type_error.317] cannot serialize binary data to text JSON");
-        CHECK_THROWS_WITH(object.dump(), "[json.exception.type_error.317] cannot serialize binary data to text JSON");
-        CHECK_THROWS_WITH(array.dump(), "[json.exception.type_error.317] cannot serialize binary data to text JSON");
+    json object = {{"key", binary}};
+    json object_empty = {{"key", binary_empty}};
+    json object_with_subtype = {{"key", binary_with_subtype}};
+    json object_empty_with_subtype = {{"key", binary_empty_with_subtype}};
+
+    json array = {"value", 1, binary};
+    json array_empty = {"value", 1, binary_empty};
+    json array_with_subtype = {"value", 1, binary_with_subtype};
+    json array_empty_with_subtype = {"value", 1, binary_empty_with_subtype};
+
+    SECTION("normal")
+    {
+        CHECK(binary.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":null}");
+        CHECK(binary_empty.dump() == "{\"bytes\":[],\"subtype\":null}");
+        CHECK(binary_with_subtype.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":128}");
+        CHECK(binary_empty_with_subtype.dump() == "{\"bytes\":[],\"subtype\":128}");
+
+        CHECK(object.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":null}}");
+        CHECK(object_empty.dump() == "{\"key\":{\"bytes\":[],\"subtype\":null}}");
+        CHECK(object_with_subtype.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":128}}");
+        CHECK(object_empty_with_subtype.dump() == "{\"key\":{\"bytes\":[],\"subtype\":128}}");
+
+        CHECK(array.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":null}]");
+        CHECK(array_empty.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":null}]");
+        CHECK(array_with_subtype.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":128}]");
+        CHECK(array_empty_with_subtype.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":128}]");
     }
 
-    SECTION("serialize_binary = true")
+    SECTION("pretty-printed")
     {
-        auto binary = json::binary_array({1, 2, 3, 4});
-        auto binary_empty = json::binary_array({});
-        json object = {{"key", binary}};
-        json array = {"value", 1, binary};
-
-        CHECK(binary.dump(-1, ' ', false, json::error_handler_t::strict, true) == "b[1,2,3,4]");
-        CHECK(binary_empty.dump(-1, ' ', false, json::error_handler_t::strict, true) == "b[]");
-        CHECK(object.dump(-1, ' ', false, json::error_handler_t::strict, true) == "{\"key\":b[1,2,3,4]}");
-        CHECK(array.dump(-1, ' ', false, json::error_handler_t::strict, true) == "[\"value\",1,b[1,2,3,4]]");
-    }
-
-    SECTION("serialize_binary = true, pretty-printed")
-    {
-        auto binary = json::binary_array({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20});
-        auto binary_empty = json::binary_array({});
-        json object = {{"key", binary}};
-        json array = {"value", 1, binary};
-
-        CHECK(binary.dump(4, ' ', false, json::error_handler_t::strict, true) == "b[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]");
-        CHECK(binary_empty.dump(4, ' ', false, json::error_handler_t::strict, true) == "b[]");
-        CHECK(object.dump(4, ' ', false, json::error_handler_t::strict, true) == "{\n"
-              "    \"key\": b[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]\n"
+        CHECK(binary.dump(4) == "{\n"
+              "    \"bytes\": [1, 2, 3, 4],\n"
+              "    \"subtype\": null\n"
               "}");
-        CHECK(array.dump(4, ' ', false, json::error_handler_t::strict, true) == "[\n"
+        CHECK(binary_empty.dump(4) == "{\n"
+              "    \"bytes\": [],\n"
+              "    \"subtype\": null\n"
+              "}");
+        CHECK(binary_with_subtype.dump(4) == "{\n"
+              "    \"bytes\": [1, 2, 3, 4],\n"
+              "    \"subtype\": 128\n"
+              "}");
+        CHECK(binary_empty_with_subtype.dump(4) == "{\n"
+              "    \"bytes\": [],\n"
+              "    \"subtype\": 128\n"
+              "}");
+
+        CHECK(object.dump(4) == "{\n"
+              "    \"key\": {\n"
+              "        \"bytes\": [1, 2, 3, 4],\n"
+              "        \"subtype\": null\n"
+              "    }\n"
+              "}");
+        CHECK(object_empty.dump(4) == "{\n"
+              "    \"key\": {\n"
+              "        \"bytes\": [],\n"
+              "        \"subtype\": null\n"
+              "    }\n"
+              "}");
+        CHECK(object_with_subtype.dump(4) == "{\n"
+              "    \"key\": {\n"
+              "        \"bytes\": [1, 2, 3, 4],\n"
+              "        \"subtype\": 128\n"
+              "    }\n"
+              "}");
+        CHECK(object_empty_with_subtype.dump(4) == "{\n"
+              "    \"key\": {\n"
+              "        \"bytes\": [],\n"
+              "        \"subtype\": 128\n"
+              "    }\n"
+              "}");
+
+        CHECK(array.dump(4) == "[\n"
               "    \"value\",\n"
               "    1,\n"
-              "    b[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]\n"
+              "    {\n"
+              "        \"bytes\": [1, 2, 3, 4],\n"
+              "        \"subtype\": null\n"
+              "    }\n"
+              "]");
+        CHECK(array_empty.dump(4) == "[\n"
+              "    \"value\",\n"
+              "    1,\n"
+              "    {\n"
+              "        \"bytes\": [],\n"
+              "        \"subtype\": null\n"
+              "    }\n"
+              "]");
+        CHECK(array_with_subtype.dump(4) == "[\n"
+              "    \"value\",\n"
+              "    1,\n"
+              "    {\n"
+              "        \"bytes\": [1, 2, 3, 4],\n"
+              "        \"subtype\": 128\n"
+              "    }\n"
+              "]");
+        CHECK(array_empty_with_subtype.dump(4) == "[\n"
+              "    \"value\",\n"
+              "    1,\n"
+              "    {\n"
+              "        \"bytes\": [],\n"
+              "        \"subtype\": 128\n"
+              "    }\n"
               "]");
     }
 }