BSON: serialization of non-objects is not supported
This commit is contained in:
parent
186c747a19
commit
f06c8fd8e3
7 changed files with 336 additions and 2 deletions
|
@ -220,6 +220,7 @@ json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten
|
||||||
json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
|
json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
|
||||||
json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
|
json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
|
||||||
json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
|
json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
|
||||||
|
json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |
|
||||||
|
|
||||||
@liveexample{The following code shows how a `type_error` exception can be
|
@liveexample{The following code shows how a `type_error` exception can be
|
||||||
caught.,type_error}
|
caught.,type_error}
|
||||||
|
|
|
@ -80,6 +80,10 @@ class binary_reader
|
||||||
result = parse_ubjson_internal();
|
result = parse_ubjson_internal();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case input_format_t::bson:
|
||||||
|
result = parse_bson_internal();
|
||||||
|
break;
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
|
@ -120,6 +124,30 @@ class binary_reader
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
bool parse_bson_internal()
|
||||||
|
{
|
||||||
|
int docLen = 0;
|
||||||
|
int byte;
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
byte = get();
|
||||||
|
if (JSON_UNLIKELY(current == std::char_traits<char>::eof()))
|
||||||
|
{
|
||||||
|
if (i == 1)
|
||||||
|
{
|
||||||
|
return sax->boolean(docLen != 0x00);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
docLen |= static_cast<std::int32_t>(byte) << 8 * i;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sax->null();
|
||||||
|
get();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] get_char whether a new character should be retrieved from the
|
@param[in] get_char whether a new character should be retrieved from the
|
||||||
input (true, default) or whether the last read
|
input (true, default) or whether the last read
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace nlohmann
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
/// the supported input formats
|
/// the supported input formats
|
||||||
enum class input_format_t { json, cbor, msgpack, ubjson };
|
enum class input_format_t { json, cbor, msgpack, ubjson, bson };
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
// input adapters //
|
// input adapters //
|
||||||
|
|
|
@ -676,6 +676,37 @@ class binary_writer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@param[in] j JSON value to serialize
|
||||||
|
@pre j.type() == value_t::object
|
||||||
|
*/
|
||||||
|
void write_bson_object(const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
assert(j.type() == value_t::object);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@param[in] j JSON value to serialize
|
||||||
|
@pre j.type() == value_t::object
|
||||||
|
*/
|
||||||
|
void write_bson(const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
switch (j.type())
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
JSON_THROW(type_error::create(317, "JSON value cannot be serialized to requested format"));
|
||||||
|
break;
|
||||||
|
case value_t::discarded:
|
||||||
|
break;
|
||||||
|
case value_t::object:
|
||||||
|
write_bson_object(j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
@brief write a number to output input
|
@brief write a number to output input
|
||||||
|
@ -704,6 +735,30 @@ class binary_writer
|
||||||
oa->write_characters(vec.data(), sizeof(NumberType));
|
oa->write_characters(vec.data(), sizeof(NumberType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@brief write a number to output in little endian format
|
||||||
|
|
||||||
|
@param[in] n number of type @a NumberType
|
||||||
|
@tparam NumberType the type of the number
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
void write_number_little_endian(const NumberType n)
|
||||||
|
{
|
||||||
|
// step 1: write number to array of length NumberType
|
||||||
|
std::array<CharType, sizeof(NumberType)> vec;
|
||||||
|
std::memcpy(vec.data(), &n, sizeof(NumberType));
|
||||||
|
|
||||||
|
// step 2: write array to output (with possible reordering)
|
||||||
|
if (!is_little_endian)
|
||||||
|
{
|
||||||
|
// reverse byte order prior to conversion if necessary
|
||||||
|
std::reverse(vec.begin(), vec.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
oa->write_characters(vec.data(), sizeof(NumberType));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// UBJSON: write number (floating point)
|
// UBJSON: write number (floating point)
|
||||||
template<typename NumberType, typename std::enable_if<
|
template<typename NumberType, typename std::enable_if<
|
||||||
std::is_floating_point<NumberType>::value, int>::type = 0>
|
std::is_floating_point<NumberType>::value, int>::type = 0>
|
||||||
|
|
|
@ -6590,6 +6590,26 @@ class basic_json
|
||||||
binary_writer<char>(o).write_ubjson(j, use_size, use_type);
|
binary_writer<char>(o).write_ubjson(j, use_size, use_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static std::vector<uint8_t> to_bson(const basic_json& j)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> result;
|
||||||
|
to_bson(j, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o)
|
||||||
|
{
|
||||||
|
binary_writer<uint8_t>(o).write_bson(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void to_bson(const basic_json& j, detail::output_adapter<char> o)
|
||||||
|
{
|
||||||
|
binary_writer<char>(o).write_bson(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief create a JSON value from an input in CBOR format
|
@brief create a JSON value from an input in CBOR format
|
||||||
|
|
||||||
|
@ -6897,6 +6917,34 @@ class basic_json
|
||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static basic_json from_bson(detail::input_adapter&& i,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
basic_json result;
|
||||||
|
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||||
|
const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::bson, &sdp, strict);
|
||||||
|
return res ? result : basic_json(value_t::discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename A1, typename A2,
|
||||||
|
detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
|
||||||
|
static basic_json from_bson(A1 && a1, A2 && a2,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
basic_json result;
|
||||||
|
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||||
|
const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::bson, &sdp, strict);
|
||||||
|
return res ? result : basic_json(value_t::discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
|
@ -819,6 +819,7 @@ json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten
|
||||||
json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
|
json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
|
||||||
json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
|
json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
|
||||||
json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
|
json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
|
||||||
|
json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |
|
||||||
|
|
||||||
@liveexample{The following code shows how a `type_error` exception can be
|
@liveexample{The following code shows how a `type_error` exception can be
|
||||||
caught.,type_error}
|
caught.,type_error}
|
||||||
|
@ -1882,7 +1883,7 @@ namespace nlohmann
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
/// the supported input formats
|
/// the supported input formats
|
||||||
enum class input_format_t { json, cbor, msgpack, ubjson };
|
enum class input_format_t { json, cbor, msgpack, ubjson, bson };
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
// input adapters //
|
// input adapters //
|
||||||
|
@ -6020,6 +6021,10 @@ class binary_reader
|
||||||
result = parse_ubjson_internal();
|
result = parse_ubjson_internal();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case input_format_t::bson:
|
||||||
|
result = parse_bson_internal();
|
||||||
|
break;
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
|
@ -6060,6 +6065,30 @@ class binary_reader
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
bool parse_bson_internal()
|
||||||
|
{
|
||||||
|
int docLen = 0;
|
||||||
|
int byte;
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
byte = get();
|
||||||
|
if (JSON_UNLIKELY(current == std::char_traits<char>::eof()))
|
||||||
|
{
|
||||||
|
if (i == 1)
|
||||||
|
{
|
||||||
|
return sax->boolean(docLen != 0x00);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
docLen |= static_cast<std::int32_t>(byte) << 8 * i;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sax->null();
|
||||||
|
get();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] get_char whether a new character should be retrieved from the
|
@param[in] get_char whether a new character should be retrieved from the
|
||||||
input (true, default) or whether the last read
|
input (true, default) or whether the last read
|
||||||
|
@ -8317,6 +8346,37 @@ class binary_writer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@param[in] j JSON value to serialize
|
||||||
|
@pre j.type() == value_t::object
|
||||||
|
*/
|
||||||
|
void write_bson_object(const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
assert(j.type() == value_t::object);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@param[in] j JSON value to serialize
|
||||||
|
@pre j.type() == value_t::object
|
||||||
|
*/
|
||||||
|
void write_bson(const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
switch (j.type())
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
JSON_THROW(type_error::create(317, "JSON value cannot be serialized to requested format"));
|
||||||
|
break;
|
||||||
|
case value_t::discarded:
|
||||||
|
break;
|
||||||
|
case value_t::object:
|
||||||
|
write_bson_object(j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
@brief write a number to output input
|
@brief write a number to output input
|
||||||
|
@ -8345,6 +8405,30 @@ class binary_writer
|
||||||
oa->write_characters(vec.data(), sizeof(NumberType));
|
oa->write_characters(vec.data(), sizeof(NumberType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@brief write a number to output in little endian format
|
||||||
|
|
||||||
|
@param[in] n number of type @a NumberType
|
||||||
|
@tparam NumberType the type of the number
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
void write_number_little_endian(const NumberType n)
|
||||||
|
{
|
||||||
|
// step 1: write number to array of length NumberType
|
||||||
|
std::array<CharType, sizeof(NumberType)> vec;
|
||||||
|
std::memcpy(vec.data(), &n, sizeof(NumberType));
|
||||||
|
|
||||||
|
// step 2: write array to output (with possible reordering)
|
||||||
|
if (!is_little_endian)
|
||||||
|
{
|
||||||
|
// reverse byte order prior to conversion if necessary
|
||||||
|
std::reverse(vec.begin(), vec.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
oa->write_characters(vec.data(), sizeof(NumberType));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// UBJSON: write number (floating point)
|
// UBJSON: write number (floating point)
|
||||||
template<typename NumberType, typename std::enable_if<
|
template<typename NumberType, typename std::enable_if<
|
||||||
std::is_floating_point<NumberType>::value, int>::type = 0>
|
std::is_floating_point<NumberType>::value, int>::type = 0>
|
||||||
|
@ -17663,6 +17747,26 @@ class basic_json
|
||||||
binary_writer<char>(o).write_ubjson(j, use_size, use_type);
|
binary_writer<char>(o).write_ubjson(j, use_size, use_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static std::vector<uint8_t> to_bson(const basic_json& j)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> result;
|
||||||
|
to_bson(j, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o)
|
||||||
|
{
|
||||||
|
binary_writer<uint8_t>(o).write_bson(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void to_bson(const basic_json& j, detail::output_adapter<char> o)
|
||||||
|
{
|
||||||
|
binary_writer<char>(o).write_bson(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief create a JSON value from an input in CBOR format
|
@brief create a JSON value from an input in CBOR format
|
||||||
|
|
||||||
|
@ -17970,6 +18074,34 @@ class basic_json
|
||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static basic_json from_bson(detail::input_adapter&& i,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
basic_json result;
|
||||||
|
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||||
|
const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::bson, &sdp, strict);
|
||||||
|
return res ? result : basic_json(value_t::discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename A1, typename A2,
|
||||||
|
detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
|
||||||
|
static basic_json from_bson(A1 && a1, A2 && a2,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
basic_json result;
|
||||||
|
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||||
|
const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::bson, &sdp, strict);
|
||||||
|
return res ? result : basic_json(value_t::discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
70
test/src/unit-bson.cpp
Normal file
70
test/src/unit-bson.cpp
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
__ _____ _____ _____
|
||||||
|
__| | __| | | | JSON for Modern C++ (test suite)
|
||||||
|
| | |__ | | | | | | version 3.2.0
|
||||||
|
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
|
||||||
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
Copyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
TEST_CASE("BSON")
|
||||||
|
{
|
||||||
|
SECTION("individual values")
|
||||||
|
{
|
||||||
|
SECTION("discarded")
|
||||||
|
{
|
||||||
|
// discarded values are not serialized
|
||||||
|
json j = json::value_t::discarded;
|
||||||
|
const auto result = json::to_bson(j);
|
||||||
|
CHECK(result.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("null")
|
||||||
|
{
|
||||||
|
json j = nullptr;
|
||||||
|
REQUIRE_THROWS_AS(json::to_bson(j), json::type_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("boolean")
|
||||||
|
{
|
||||||
|
SECTION("true")
|
||||||
|
{
|
||||||
|
json j = true;
|
||||||
|
REQUIRE_THROWS_AS(json::to_bson(j), json::type_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("false")
|
||||||
|
{
|
||||||
|
json j = false;
|
||||||
|
REQUIRE_THROWS_AS(json::to_bson(j), json::type_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue