mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 11:40:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80475865c0 |
+1
-1
@@ -10,5 +10,5 @@ title: "JSON for Modern C++"
|
||||
version: 3.12.0
|
||||
date-released: 2025-04-07
|
||||
license: MIT
|
||||
repository-code: "https://github.com/nlohmann/json"
|
||||
repository-code: "https://github.com/nlohmann"
|
||||
url: https://json.nlohmann.me
|
||||
|
||||
+1
-4
@@ -42,11 +42,8 @@ endif()
|
||||
## OPTIONS
|
||||
##
|
||||
|
||||
# Build the tests by default only for the main project and only if the tests
|
||||
# directory exists (the release archive json.tar.xz does not contain it).
|
||||
# VERSION_GREATER_EQUAL is not available in older CMake (< 3.7)
|
||||
if(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13)
|
||||
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt")
|
||||
if(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13))
|
||||
set(JSON_BuildTests_INIT ON)
|
||||
else()
|
||||
set(JSON_BuildTests_INIT OFF)
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
# find GNU sed to use `-i` parameter
|
||||
SED:=$(shell command -v gsed || which sed)
|
||||
|
||||
# find GNU tar to use `--sort` and `--pax-option` parameters
|
||||
TAR:=$(shell command -v gtar || which tar)
|
||||
|
||||
|
||||
##########################################################################
|
||||
# source files
|
||||
@@ -218,8 +215,8 @@ ChangeLog.md:
|
||||
# archive is created according to the advices of <https://reproducible-builds.org/docs/archives/>.
|
||||
json.tar.xz:
|
||||
mkdir json
|
||||
rsync -R $(shell find LICENSE.MIT nlohmann_json.natvis CMakeLists.txt cmake/*.in include single_include src/modules -type f) json
|
||||
$(TAR) --sort=name --mtime="@$(shell git log -1 --pretty=%ct)" --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime --create --file - json | xz --compress -9e --threads=2 - > json.tar.xz
|
||||
rsync -R $(shell find LICENSE.MIT nlohmann_json.natvis CMakeLists.txt cmake/*.in include single_include -type f) json
|
||||
gtar --sort=name --mtime="@$(shell git log -1 --pretty=%ct)" --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime --create --file - json | xz --compress -9e --threads=2 - > json.tar.xz
|
||||
rm -fr json
|
||||
|
||||
# We use `-X` to make the resulting ZIP file reproducible, see
|
||||
|
||||
@@ -1204,18 +1204,15 @@ language bindings, format converters, and the like. See the curated [Ecosystem](
|
||||
|
||||
Though it's 2026 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
|
||||
|
||||
- GCC 4.8 - 16.2 (and possibly later)
|
||||
- Clang 3.4 - 22.1 (and possibly later)
|
||||
- Apple Clang 15.0 - 21.0 (and possibly later)
|
||||
- Intel C++ Compiler Classic (icpc) 2021.10
|
||||
- Intel oneAPI DPC++/C++ Compiler (icpx) 2025.3 (and possibly later)
|
||||
- NVIDIA CUDA Compiler (nvcc) 11.8 - 12.6 (and possibly later)
|
||||
- NVIDIA HPC SDK C++ Compiler (nvc++) 25.5 (and possibly later)
|
||||
- Microsoft Visual C++ 2015 / MSVC 19.0 (and possibly later)
|
||||
- Microsoft Visual C++ 2017 / MSVC 19.16 (and possibly later)
|
||||
- Microsoft Visual C++ 2019 / MSVC 19.29 (and possibly later)
|
||||
- Microsoft Visual C++ 2022 / MSVC 19.44 (and possibly later)
|
||||
- Microsoft Visual C++ 2026 / MSVC 19.51 (and possibly later)
|
||||
- GCC 4.8 - 14.2 (and possibly later)
|
||||
- Clang 3.4 - 21.0 (and possibly later)
|
||||
- Apple Clang 9.1 - 16.0 (and possibly later)
|
||||
- Intel C++ Compiler 17.0.2 (and possibly later)
|
||||
- Nvidia CUDA Compiler 11.0.221 (and possibly later)
|
||||
- Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later)
|
||||
- Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later)
|
||||
- Microsoft Visual C++ 2019 / Build Tools 16.3.1+1def00d3d (and possibly later)
|
||||
- Microsoft Visual C++ 2022 / Build Tools 19.30.30709.0 (and possibly later)
|
||||
|
||||
I would be happy to learn about other compilers/versions.
|
||||
|
||||
|
||||
@@ -88,7 +88,12 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
||||
## Exceptions
|
||||
|
||||
- Throws [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token, or
|
||||
empty input like a null `FILE*` or `char*` pointer.
|
||||
empty input like a null `FILE*` or `char*` pointer, or an `std::istream` without a stream buffer
|
||||
(`#!cpp i.rdbuf() == nullptr`, for instance `#!cpp std::istream(nullptr)`).
|
||||
- If reading from an `std::istream` reaches the end of the input and `eofbit` is part of the stream's
|
||||
[`exceptions()`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions) mask, the `std::ios_base::failure`
|
||||
thrown by the stream itself propagates instead of a `parse_error`, the same as it would for the standard library's
|
||||
own extraction operators.
|
||||
|
||||
## Complexity
|
||||
|
||||
@@ -254,6 +259,8 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
- `JSON_STRICT_NUL_HANDLING` added in version 3.13.0 to optionally reject a NUL byte in the input instead of treating
|
||||
it as end of input; planned to become the default in version 4.0.0.
|
||||
- Extended empty-input detection to also cover an `std::istream` without a stream buffer, and fixed a crash
|
||||
(`std::terminate`) when parsing from an `std::istream` with `eofbit` in its exception mask, in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -20,7 +20,12 @@ the stream `i`
|
||||
|
||||
## Exceptions
|
||||
|
||||
- Throws [`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token.
|
||||
- Throws [`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token, or if
|
||||
`i` has no stream buffer (`#!cpp i.rdbuf() == nullptr`, for instance `#!cpp std::istream(nullptr)`).
|
||||
- If reading from `i` reaches the end of the input and `eofbit` is part of `i`'s
|
||||
[`exceptions()`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions) mask, the `std::ios_base::failure`
|
||||
thrown by `i` itself propagates instead of a `parse_error`, the same as it would for the standard library's own
|
||||
extraction operators.
|
||||
|
||||
## Complexity
|
||||
|
||||
@@ -118,3 +123,5 @@ being read.
|
||||
it as end of input; planned to become the default in version 4.0.0.
|
||||
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave the character that terminates a number in
|
||||
the stream; planned to become the default in version 4.0.0.
|
||||
- Fixed a null pointer dereference for an `std::istream` without a stream buffer (now throws `parse_error.101`), and a
|
||||
crash (`std::terminate`) when `i` has `eofbit` in its exception mask, in version 3.13.0.
|
||||
|
||||
@@ -125,7 +125,7 @@ automatically download a release as a dependency at configure time.
|
||||
|
||||
### `JSON_BuildTests`
|
||||
|
||||
Build the unit tests when [`BUILD_TESTING`](https://cmake.org/cmake/help/latest/command/enable_testing.html) is enabled. This option is `ON` by default if the library's CMake project is the top project and the `tests` directory exists (the release archive `json.tar.xz` does not contain it). That is, when integrating the library as described above, the test suite is not built unless explicitly switched on with this option.
|
||||
Build the unit tests when [`BUILD_TESTING`](https://cmake.org/cmake/help/latest/command/enable_testing.html) is enabled. This option is `ON` by default if the library's CMake project is the top project. That is, when integrating the library as described above, the test suite is not built unless explicitly switched on with this option.
|
||||
|
||||
### `JSON_CI`
|
||||
|
||||
|
||||
@@ -106,7 +106,13 @@ class input_stream_adapter
|
||||
// was given back with release_lookahead()
|
||||
commit_lookahead();
|
||||
#endif
|
||||
is->clear(is->rdstate() & std::ios::eofbit);
|
||||
// only call clear() if there is something to clear: it throws
|
||||
// std::ios_base::failure if the stream has exceptions() enabled
|
||||
// for a state bit that remains set, and a destructor must not throw
|
||||
if ((is->rdstate() & ~std::ios::eofbit) != 0)
|
||||
{
|
||||
is->clear(is->rdstate() & std::ios::eofbit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -811,12 +817,16 @@ inline file_input_adapter input_adapter(std::FILE* file)
|
||||
|
||||
inline input_stream_adapter input_adapter(std::istream& stream)
|
||||
{
|
||||
if (stream.rdbuf() == nullptr)
|
||||
{
|
||||
JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||
}
|
||||
return input_stream_adapter(stream);
|
||||
}
|
||||
|
||||
inline input_stream_adapter input_adapter(std::istream&& stream)
|
||||
{
|
||||
return input_stream_adapter(stream);
|
||||
return input_adapter(stream);
|
||||
}
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
|
||||
@@ -7650,7 +7650,13 @@ class input_stream_adapter
|
||||
// was given back with release_lookahead()
|
||||
commit_lookahead();
|
||||
#endif
|
||||
is->clear(is->rdstate() & std::ios::eofbit);
|
||||
// only call clear() if there is something to clear: it throws
|
||||
// std::ios_base::failure if the stream has exceptions() enabled
|
||||
// for a state bit that remains set, and a destructor must not throw
|
||||
if ((is->rdstate() & ~std::ios::eofbit) != 0)
|
||||
{
|
||||
is->clear(is->rdstate() & std::ios::eofbit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8355,12 +8361,16 @@ inline file_input_adapter input_adapter(std::FILE* file)
|
||||
|
||||
inline input_stream_adapter input_adapter(std::istream& stream)
|
||||
{
|
||||
if (stream.rdbuf() == nullptr)
|
||||
{
|
||||
JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||
}
|
||||
return input_stream_adapter(stream);
|
||||
}
|
||||
|
||||
inline input_stream_adapter input_adapter(std::istream&& stream)
|
||||
{
|
||||
return input_stream_adapter(stream);
|
||||
return input_adapter(stream);
|
||||
}
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
|
||||
@@ -388,6 +388,37 @@ TEST_CASE("deserialization")
|
||||
}));
|
||||
}
|
||||
|
||||
SECTION("stream with eofbit in its exception mask (issue #5646)")
|
||||
{
|
||||
// reaching EOF while parsing a value that fills the whole input
|
||||
// (e.g., a number, or any value under strict parsing) makes
|
||||
// get_character() call std::istream::clear() to record eofbit;
|
||||
// with eofbit in the exception mask, that clear() itself throws
|
||||
// std::ios_base::failure - it must propagate to the caller instead
|
||||
// of ~input_stream_adapter() throwing a second exception while the
|
||||
// first is still unwinding, which would call std::terminate
|
||||
json _;
|
||||
|
||||
std::istringstream is1("1");
|
||||
is1.exceptions(std::ios::eofbit);
|
||||
CHECK_THROWS_AS(_ = json::parse(is1), std::ios_base::failure&);
|
||||
|
||||
// the same holds for the common std::ifstream::exceptions(failbit |
|
||||
// badbit | eofbit) pattern, because only eofbit ends up set
|
||||
std::istringstream is2("1");
|
||||
is2.exceptions(std::ios::failbit | std::ios::badbit | std::ios::eofbit);
|
||||
CHECK_THROWS_AS(_ = json::parse(is2), std::ios_base::failure&);
|
||||
}
|
||||
|
||||
SECTION("stream without a streambuf (issue #5646)")
|
||||
{
|
||||
// std::istream(nullptr) has badbit set and rdbuf() == nullptr;
|
||||
// get_character() must not dereference that null streambuf
|
||||
std::istream is(nullptr);
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse(is), "[json.exception.parse_error.101] parse error: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json::string_t const s = R"(["foo",1,2,3,false,{"one":1})";
|
||||
|
||||
@@ -234,4 +234,33 @@ TEST_CASE("JSON_PRECISE_STREAM_POSITION")
|
||||
CHECK(j == json(1));
|
||||
CHECK(remaining(is) == "true");
|
||||
}
|
||||
|
||||
SECTION("stream with eofbit in its exception mask (issue #5646)")
|
||||
{
|
||||
// with JSON_PRECISE_STREAM_POSITION, get_character() peeks via
|
||||
// sb->sgetc() rather than consuming via sb->sbumpc(), but it still
|
||||
// calls std::istream::clear() to record eofbit once the streambuf is
|
||||
// exhausted; with eofbit in the exception mask, that clear() itself
|
||||
// throws std::ios_base::failure, which must propagate to the caller
|
||||
// instead of ~input_stream_adapter() throwing a second exception
|
||||
// while the first is still unwinding (which would call std::terminate)
|
||||
json _;
|
||||
|
||||
std::istringstream is1("1");
|
||||
is1.exceptions(std::ios::eofbit);
|
||||
CHECK_THROWS_AS(_ = json::parse(is1), std::ios_base::failure&);
|
||||
|
||||
std::istringstream is2("1");
|
||||
is2.exceptions(std::ios::failbit | std::ios::badbit | std::ios::eofbit);
|
||||
CHECK_THROWS_AS(_ = json::parse(is2), std::ios_base::failure&);
|
||||
}
|
||||
|
||||
SECTION("stream without a streambuf (issue #5646)")
|
||||
{
|
||||
// std::istream(nullptr) has badbit set and rdbuf() == nullptr;
|
||||
// get_character() must not dereference that null streambuf
|
||||
std::istream is(nullptr);
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse(is), "[json.exception.parse_error.101] parse error: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user