Files
json/docs/mkdocs/docs/home/releases/unreleased.md
T
Niels Lohmann cdfb130fa6 Read a std::u8string from a JSON string and store it as one in every configuration (#6075)
* Read a std::u8string from a JSON string and store it as one in every configuration

In C++20, json(std::u8string) was stored as a JSON string only when
std::filesystem::path support was available (the to_json overload sat
inside the JSON_HAS_FILESYSTEM guard), and there was no matching
from_json, so get<std::u8string>() threw type_error.302 ("type must be
array, but is string"). Without std::filesystem, the same expression
still produced an array of code units (the 3.12.0 behavior).

Move the to_json overload out of the filesystem guard so that a
std::u8string is a string in every configuration, build the value
directly from the code units (this also works for a custom string_t
such as the tests' alt_string), and add a from_json overload that reads
a JSON string. The array of numbers written by 3.12.0 is still accepted;
other types throw type_error.302 ("type must be string or array").
std::u8string_view and char8_t arrays are unchanged.

The tests now run in C++20/23 (the file mentions JSON_HAS_CPP_20/23),
also without std::filesystem (test-conversions2_no_filesystem), and with
ordered_json and a custom string type. The new behavior is documented.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fill the std::u8string with resize and indexing instead of push_back

The push_back loop is not vectorized: reading a 1 kB string took about
2.2 us against 0.39 us with resize and direct element access (clang -O2).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Write the std::u8string from_json template header in the compact form

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Ignore libstdc++ char to char8_t sign change in the clang sanitizer job

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-12 08:40:18 +01:00

1.9 KiB

Unreleased

These changes are part of the develop branch, but not of a release yet.

  • Converting an rvalue container to JSON (json j = std::move(vec);, json j = std::map<std::string, std::string>{...};) now moves the elements instead of copying them (e.g., std::vector<std::string> with 100,000 strings: 4.7 ms to 2.3 ms). Two consequences: the source container is left in a valid but unspecified (moved-from) state, and if an exception is thrown during the conversion, the source may be partially moved-from, so the strong exception guarantee no longer holds for the source of an rvalue conversion (the JSON value itself is unaffected). Lvalues and const objects are still copied. See basic_json.
  • In C++20 (library support for char8_t, __cpp_lib_char8_t), a #!cpp std::u8string is a JSON string: json j = std::u8string(u8"abc"); stores "abc", and #!cpp j.get<std::u8string>() reads it back. Version 3.12.0 stored a #!cpp std::u8string as an array of code units ([97,98,99]); on the 3.13.0 development branch it was already stored as a string if std::filesystem is available, but could not be read back. Now the result no longer depends on whether std::filesystem::path is supported, and #!cpp get<std::u8string>() accepts both a JSON string and the array of numbers written by 3.12.0. Consequence: JSON documents produced from a #!cpp std::u8string by 3.12.0 (arrays) are still read, but code that checks is_array() on such a value, or that reads it as #!cpp std::vector<char8_t> / #!cpp std::vector<std::uint8_t>, now sees a string; to keep the array form, convert explicitly, e.g., #!cpp json(std::vector<char8_t>(s.begin(), s.end())). #!cpp std::u8string_view and #!cpp char8_t arrays are unchanged (arrays of code units). See basic_json.

API changes