Skip to content

Commit

Permalink
BJData draft3 flag
Browse files Browse the repository at this point in the history
  • Loading branch information
nebkat committed Dec 5, 2024
1 parent c40c945 commit 7f12cd6
Show file tree
Hide file tree
Showing 6 changed files with 321 additions and 231 deletions.
12 changes: 9 additions & 3 deletions docs/mkdocs/docs/api/basic_json/to_bjdata.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
// (1)
static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
const bool use_size = false,
const bool use_type = false);
const bool use_type = false,
const bool draft3_binary = false);

// (2)
static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
const bool use_size = false, const bool use_type = false);
const bool use_size = false, const bool use_type = false,
const bool draft3_binary = false);
static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
const bool use_size = false, const bool use_type = false);
const bool use_size = false, const bool use_type = false,
const bool draft3_binary = false);
```
Serializes a given JSON value `j` to a byte vector using the BJData (Binary JData) serialization format. BJData aims to
Expand All @@ -34,6 +37,9 @@ The exact mapping and its limitations is described on a [dedicated page](../../f
`use_type` (in)
: whether to add type annotations to container types (must be combined with `#!cpp use_size = true`); optional,
`draft3_binary` (in)
: whether to use the draft 3 binary format (see [draft 3](../../features/binary_formats/bjdata.md#draft-3-binary-format)); optional,
`#!cpp false` by default.
## Return value
Expand Down
7 changes: 7 additions & 0 deletions docs/mkdocs/docs/features/binary_formats/bjdata.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ The library uses the following mapping from JSON values types to BJData types ac
in optimized arrays to designate binary data. This means that, unlike UBJSON, binary data can be both serialized and
deserialized.

To preserve compatibility with BJData Draft 2, the Draft 3 optimized binary array must be explicitly enabled using
the `draft3_binary` parameter of [`to_bjdata`](../../api/basic_json/to_bjdata.md).

In Draft2 mode (default), if the JSON data contains the binary type, the value stored is a list of integers, as
suggested by the BJData documentation. In particular, this means that the serialization and the deserialization of
JSON containing binary values into BJData and back will result in a different JSON object.

[BJDataBinArr]: https://github.com/NeuroJSON/bjdata/blob/master/Binary_JData_Specification.md#optimized-binary-array)

??? example
Expand Down
19 changes: 10 additions & 9 deletions include/nlohmann/detail/output/binary_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,10 +735,11 @@ class binary_writer
@param[in] use_type whether to use '$' prefixes (optimized format)
@param[in] add_prefix whether prefixes need to be used for this value
@param[in] use_bjdata whether write in BJData format, default is false
@param[in] bjdata_draft3_binary whether to use BJData draft-3 binary format, default is false
*/
void write_ubjson(const BasicJsonType& j, const bool use_count,
const bool use_type, const bool add_prefix = true,
const bool use_bjdata = false)
const bool use_bjdata = false, const bool bjdata_draft3_binary = false)
{
switch (j.type())
{
Expand Down Expand Up @@ -829,7 +830,7 @@ class binary_writer

for (const auto& el : *j.m_data.m_value.array)
{
write_ubjson(el, use_count, use_type, prefix_required, use_bjdata);
write_ubjson(el, use_count, use_type, prefix_required, use_bjdata, bjdata_draft3_binary);
}

if (!use_count)
Expand All @@ -847,11 +848,11 @@ class binary_writer
oa->write_character(to_char_type('['));
}

if (use_type && (use_bjdata || !j.m_data.m_value.binary->empty()))
if (use_type && ((use_bjdata && bjdata_draft3_binary) || !j.m_data.m_value.binary->empty()))
{
JSON_ASSERT(use_count);
oa->write_character(to_char_type('$'));
oa->write_character(use_bjdata ? 'B' : 'U');
oa->write_character(use_bjdata && bjdata_draft3_binary ? 'B' : 'U');
}

if (use_count)
Expand All @@ -870,7 +871,7 @@ class binary_writer
{
for (size_t i = 0; i < j.m_data.m_value.binary->size(); ++i)
{
oa->write_character(to_char_type(use_bjdata ? 'B' : 'U'));
oa->write_character(to_char_type((use_bjdata && bjdata_draft3_binary) ? 'B' : 'U'));
oa->write_character(j.m_data.m_value.binary->data()[i]);
}
}
Expand All @@ -887,7 +888,7 @@ class binary_writer
{
if (use_bjdata && j.m_data.m_value.object->size() == 3 && j.m_data.m_value.object->find("_ArrayType_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArraySize_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArrayData_") != j.m_data.m_value.object->end())
{
if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata)
if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type, bjdata_draft3_binary)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata)
{
break;
}
Expand Down Expand Up @@ -931,7 +932,7 @@ class binary_writer
oa->write_characters(
reinterpret_cast<const CharType*>(el.first.c_str()),
el.first.size());
write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata);
write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_draft3_binary);
}

if (!use_count)
Expand Down Expand Up @@ -1615,7 +1616,7 @@ class binary_writer
/*!
@return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
*/
bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type)
bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type, const bool bjdata_draft3_binary)
{
std::map<string_t, CharType> bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'},
{"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'},
Expand Down Expand Up @@ -1649,7 +1650,7 @@ class binary_writer
oa->write_character('#');

key = "_ArraySize_";
write_ubjson(value.at(key), use_count, use_type, true, true);
write_ubjson(value.at(key), use_count, use_type, true, true, bjdata_draft3_binary);

key = "_ArrayData_";
if (dtype == 'U' || dtype == 'C' || dtype == 'B')
Expand Down
15 changes: 9 additions & 6 deletions include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4309,27 +4309,30 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
const bool use_size = false,
const bool use_type = false)
const bool use_type = false,
const bool draft3_binary = false)
{
std::vector<std::uint8_t> result;
to_bjdata(j, result, use_size, use_type);
to_bjdata(j, result, use_size, use_type, draft3_binary);
return result;
}

/// @brief create a BJData serialization of a given JSON value
/// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
const bool use_size = false, const bool use_type = false)
const bool use_size = false, const bool use_type = false,
const bool draft3_binary = false)
{
binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true);
binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true, draft3_binary);
}

/// @brief create a BJData serialization of a given JSON value
/// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
const bool use_size = false, const bool use_type = false)
const bool use_size = false, const bool use_type = false,
const bool draft3_binary = false)
{
binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true);
binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true, draft3_binary);
}

/// @brief create a BSON serialization of a given JSON value
Expand Down
34 changes: 19 additions & 15 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15989,10 +15989,11 @@ class binary_writer
@param[in] use_type whether to use '$' prefixes (optimized format)
@param[in] add_prefix whether prefixes need to be used for this value
@param[in] use_bjdata whether write in BJData format, default is false
@param[in] bjdata_draft3_binary whether to use BJData draft-3 binary format, default is false
*/
void write_ubjson(const BasicJsonType& j, const bool use_count,
const bool use_type, const bool add_prefix = true,
const bool use_bjdata = false)
const bool use_bjdata = false, const bool bjdata_draft3_binary = false)
{
switch (j.type())
{
Expand Down Expand Up @@ -16083,7 +16084,7 @@ class binary_writer

for (const auto& el : *j.m_data.m_value.array)
{
write_ubjson(el, use_count, use_type, prefix_required, use_bjdata);
write_ubjson(el, use_count, use_type, prefix_required, use_bjdata, bjdata_draft3_binary);
}

if (!use_count)
Expand All @@ -16101,11 +16102,11 @@ class binary_writer
oa->write_character(to_char_type('['));
}

if (use_type && (use_bjdata || !j.m_data.m_value.binary->empty()))
if (use_type && ((use_bjdata && bjdata_draft3_binary) || !j.m_data.m_value.binary->empty()))
{
JSON_ASSERT(use_count);
oa->write_character(to_char_type('$'));
oa->write_character(use_bjdata ? 'B' : 'U');
oa->write_character(use_bjdata && bjdata_draft3_binary ? 'B' : 'U');
}

if (use_count)
Expand All @@ -16124,7 +16125,7 @@ class binary_writer
{
for (size_t i = 0; i < j.m_data.m_value.binary->size(); ++i)
{
oa->write_character(to_char_type(use_bjdata ? 'B' : 'U'));
oa->write_character(to_char_type((use_bjdata && bjdata_draft3_binary) ? 'B' : 'U'));
oa->write_character(j.m_data.m_value.binary->data()[i]);
}
}
Expand All @@ -16141,7 +16142,7 @@ class binary_writer
{
if (use_bjdata && j.m_data.m_value.object->size() == 3 && j.m_data.m_value.object->find("_ArrayType_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArraySize_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArrayData_") != j.m_data.m_value.object->end())
{
if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata)
if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type, bjdata_draft3_binary)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata)
{
break;
}
Expand Down Expand Up @@ -16185,7 +16186,7 @@ class binary_writer
oa->write_characters(
reinterpret_cast<const CharType*>(el.first.c_str()),
el.first.size());
write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata);
write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_draft3_binary);
}

if (!use_count)
Expand Down Expand Up @@ -16869,7 +16870,7 @@ class binary_writer
/*!
@return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
*/
bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type)
bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type, const bool bjdata_draft3_binary)
{
std::map<string_t, CharType> bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'},
{"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'},
Expand Down Expand Up @@ -16903,7 +16904,7 @@ class binary_writer
oa->write_character('#');

key = "_ArraySize_";
write_ubjson(value.at(key), use_count, use_type, true, true);
write_ubjson(value.at(key), use_count, use_type, true, true, bjdata_draft3_binary);

key = "_ArrayData_";
if (dtype == 'U' || dtype == 'C' || dtype == 'B')
Expand Down Expand Up @@ -23827,27 +23828,30 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
const bool use_size = false,
const bool use_type = false)
const bool use_type = false,
const bool draft3_binary = false)
{
std::vector<std::uint8_t> result;
to_bjdata(j, result, use_size, use_type);
to_bjdata(j, result, use_size, use_type, draft3_binary);
return result;
}

/// @brief create a BJData serialization of a given JSON value
/// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
const bool use_size = false, const bool use_type = false)
const bool use_size = false, const bool use_type = false,
const bool draft3_binary = false)
{
binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true);
binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true, draft3_binary);
}

/// @brief create a BJData serialization of a given JSON value
/// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
const bool use_size = false, const bool use_type = false)
const bool use_size = false, const bool use_type = false,
const bool draft3_binary = false)
{
binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true);
binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true, draft3_binary);
}

/// @brief create a BSON serialization of a given JSON value
Expand Down
Loading

0 comments on commit 7f12cd6

Please sign in to comment.