Skip to content

Commit c9f1ad1

Browse files
committed
CXX-595 Implement a basic from_json
1 parent 277b8fd commit c9f1ad1

File tree

4 files changed

+66
-1
lines changed

4 files changed

+66
-1
lines changed

src/bsoncxx/json.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <sstream>
2121
#include <vector>
2222

23+
#include <bson.h>
24+
2325
#include <bsoncxx/document/view.hpp>
2426
#include <bsoncxx/stdx/make_unique.hpp>
2527
#include <bsoncxx/stdx/string_view.hpp>
@@ -33,6 +35,10 @@ BSONCXX_INLINE_NAMESPACE_BEGIN
3335

3436
namespace {
3537

38+
void bson_free_deleter(std::uint8_t* ptr) {
39+
bson_free(ptr);
40+
}
41+
3642
class json_visitor {
3743
public:
3844
json_visitor(std::ostream& out, bool is_array, std::size_t padding)
@@ -275,5 +281,19 @@ std::string to_json(types::value value) {
275281
return ss.str();
276282
}
277283

284+
stdx::optional<document::value> from_json(stdx::string_view json) {
285+
bson_error_t error;
286+
bson_t* result = bson_new_from_json(
287+
reinterpret_cast<const uint8_t*>(json.data()), json.size(), &error);
288+
289+
if (!result)
290+
return stdx::nullopt;
291+
292+
std::uint32_t length;
293+
std::uint8_t* buf = bson_destroy_with_steal(result, true, &length);
294+
295+
return document::value{buf, length, bson_free_deleter};
296+
}
297+
278298
BSONCXX_INLINE_NAMESPACE_END
279299
} // namespace bsoncxx

src/bsoncxx/json.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
#include <string>
2020

21+
#include <bsoncxx/document/value.hpp>
2122
#include <bsoncxx/document/view.hpp>
23+
#include <bsoncxx/stdx/optional.hpp>
2224

2325
namespace bsoncxx {
2426
BSONCXX_INLINE_NAMESPACE_BEGIN
@@ -55,6 +57,16 @@ BSONCXX_API std::string to_json(document::element element);
5557
///
5658
BSONCXX_API std::string to_json(types::value value);
5759

60+
///
61+
/// Constructs a new document::value from the provided JSON text
62+
///
63+
/// @param 'json'
64+
/// A string_view into a JSON document
65+
///
66+
/// @returns An engaged optional containing a document::value if conversion worked.
67+
///
68+
BSONCXX_API stdx::optional<document::value> from_json(stdx::string_view json);
69+
5870
BSONCXX_INLINE_NAMESPACE_END
5971
} // namespace bsoncxx
6072

src/bsoncxx/test/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ include_directories(
33
)
44

55
add_executable(test_bson
6+
bson_builder.cpp
67
bson_util_itoa.cpp
78
bson_validate.cpp
9+
json.cpp
810
new_tests.cpp
9-
bson_builder.cpp
1011
)
1112

1213
target_link_libraries(test_bson bsoncxx_static)

src/bsoncxx/test/json.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include "catch.hpp"
2+
3+
#include <bsoncxx/builder/stream/document.hpp>
4+
#include <bsoncxx/json.hpp>
5+
6+
namespace {
7+
constexpr auto k_invalid_json = R"({])";
8+
constexpr auto k_valid_json = R"({ "a" : 1, "b" : 2.0 })";
9+
}
10+
11+
TEST_CASE("invalid json returns disengaged optional") {
12+
using namespace bsoncxx;
13+
REQUIRE(!from_json(k_invalid_json));
14+
}
15+
16+
TEST_CASE("valid json returns an engaged optional") {
17+
using namespace bsoncxx;
18+
REQUIRE(from_json(k_valid_json));
19+
}
20+
21+
TEST_CASE("valid json is converted to equivalent BSON") {
22+
using namespace bsoncxx;
23+
24+
const auto expected = builder::stream::document{} << "a" << 1 << "b" << 2.0 << builder::stream::finalize;
25+
const auto expected_view = expected.view();
26+
27+
const auto actual = from_json(k_valid_json);
28+
const auto actual_view = actual->view();
29+
30+
REQUIRE(expected_view.length() == actual_view.length());
31+
REQUIRE(0 == memcmp(expected_view.data(), actual_view.data(), expected_view.length()));
32+
}

0 commit comments

Comments
 (0)