mirror of
https://github.com/nlohmann/json.git
synced 2026-10-31 18:05:44 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07ac676393 | ||
|
|
1db091b784 |
No files matched your search
@@ -253,6 +253,34 @@ Two conversions never copy at all:
|
||||
|
||||
Both results are only valid as long as the view — and, for a string with no escapes, the borrowed source text — is.
|
||||
|
||||
### `string_view` members
|
||||
|
||||
A struct whose member is a `#!cpp std::string_view` (C++17) can be decoded from a view with the
|
||||
`NLOHMANN_DEFINE_TYPE_*` macros, and the member is not copied either: for a string without escape sequences it points
|
||||
into the document's source text, and for a string with escape sequences into the document's buffer of decoded strings.
|
||||
The member is valid as long as the document is alive and, for a document that
|
||||
[borrows its source text](#ownership-and-lifetime) (as [`parse()`](../api/basic_json_document/parse.md) does for
|
||||
an lvalue `#!cpp std::string`, a `#!cpp std::string_view`, or a C string), as long as that text is.
|
||||
|
||||
```cpp
|
||||
struct record
|
||||
{
|
||||
std::string_view name;
|
||||
int count;
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(record, name, count)
|
||||
};
|
||||
|
||||
const std::string text = R"({"name": "widget", "count": 3})";
|
||||
const json_document doc = json_document::parse(text); // borrows text
|
||||
const record r = doc.root().get<record>(); // r.name points into text, valid while text and doc are
|
||||
```
|
||||
|
||||
!!! warning "Decoding into a `string_view` from a `json` value dangles at once"
|
||||
|
||||
The same struct also compiles with `#!cpp json::parse(text).get<record>()`, but there `name` points into the
|
||||
temporary `json` value, which is destroyed at the end of the statement. Use a named
|
||||
[`json_document`](../api/json_document.md), as above, to decode such a struct.
|
||||
|
||||
## Writing generic code
|
||||
|
||||
A view's type names its `BasicJsonType` and its [`view_mode`](../api/view_mode.md):
|
||||
|
||||
@@ -39,6 +39,7 @@ using nlohmann::ordered_json_document;
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair; same() compares floating-point values on purpose
|
||||
@@ -469,5 +470,70 @@ TEST_CASE("json_view from_json")
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
namespace view_from_json
|
||||
{
|
||||
// a string_view member is not copied: it points into the source text or, for a
|
||||
// string with escape sequences, into the buffer of decoded strings of the document
|
||||
struct named_view
|
||||
{
|
||||
// initialized for GCC -Weffc++
|
||||
// NOLINTNEXTLINE(readability-redundant-member-init)
|
||||
std::string_view name{};
|
||||
int n = 0;
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(named_view, name, n)
|
||||
};
|
||||
} // namespace view_from_json
|
||||
|
||||
TEST_CASE("json_view from_json: string_view members borrow from the document")
|
||||
{
|
||||
using view_from_json::named_view;
|
||||
|
||||
// whether all of s is inside the bytes of text
|
||||
const auto within = [](std::string_view s, std::string_view text)
|
||||
{
|
||||
const auto first = reinterpret_cast<std::uintptr_t>(text.data());
|
||||
const auto begin = reinterpret_cast<std::uintptr_t>(s.data());
|
||||
return begin >= first && begin + s.size() <= first + text.size();
|
||||
};
|
||||
|
||||
SECTION("a string without escapes points into the source text")
|
||||
{
|
||||
const std::string text = R"({"name": "hello", "n": 1})";
|
||||
const json_document d = json_document::parse(text);
|
||||
REQUIRE_FALSE(d.owns_source());
|
||||
const auto r = d.root().get<named_view>();
|
||||
CHECK(r.name == "hello");
|
||||
CHECK(r.n == 1);
|
||||
CHECK(within(r.name, text));
|
||||
CHECK(within(r.name, d.source()));
|
||||
}
|
||||
|
||||
SECTION("a string with escapes points into the document, not into the source text")
|
||||
{
|
||||
const std::string text = R"({"name": "a\nb", "n": 2})";
|
||||
json_document d = json_document::parse(text);
|
||||
REQUIRE_FALSE(d.owns_source());
|
||||
const auto r = d.root().get<named_view>();
|
||||
CHECK(r.name == "a\nb");
|
||||
CHECK(r.n == 2);
|
||||
CHECK_FALSE(within(r.name, text));
|
||||
// valid as long as the document is alive, even if the document is moved
|
||||
const json_document moved = std::move(d);
|
||||
CHECK(r.name == "a\nb");
|
||||
CHECK(moved.root().get<named_view>().name == r.name);
|
||||
}
|
||||
|
||||
SECTION("a document that owns its text keeps the members readable while it is alive")
|
||||
{
|
||||
const json_document d = json_document::parse(std::string(R"({"name": "owned", "n": 3})"));
|
||||
REQUIRE(d.owns_source());
|
||||
const auto r = d.root().get<named_view>();
|
||||
CHECK(r.name == "owned");
|
||||
CHECK(within(r.name, d.source()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
DOCTEST_GCC_SUPPRESS_WARNING_POP
|
||||
Reference in new issue
Block a user