Files
json/docs/mkdocs/docs/home/faq.md
T
Niels LohmannandClaude Sonnet 5 633eef8494 fix: treat a NUL byte in the input as an ordinary byte, not EOF
The lexer's token dispatch had `case '\0':` fall through to the same
`end_of_input` handling as the real end-of-file sentinel, with a comment
claiming the NUL case was "needed when parsing from string literals".
That rationale no longer holds: input_adapter(const char*) already uses
strlen() to compute its range, so it never hands the lexer a trailing
NUL, and the const-char* overload is the only "string literal" path the
comment could be referring to. In practice, `case '\0':` only ever fired
on a genuine embedded or trailing NUL byte in real input data (e.g. a
std::string with '\0' appended), which was then silently swallowed as if
it were EOF instead of producing the parse_error.101 any other
unexpected byte gets. Two more spots in the comment-skipping logic had
the same NUL-as-EOF idiom, stopping a `//` or `/* */` comment scan early
at an embedded NUL instead of continuing to the real terminator.

Removing all three still left one real regression: input_adapter's
T(&array)[N] overload (used for a string literal like
json::parse("123"), as opposed to a decayed const char* pointer) passes
the array's full extent through unchanged, trailing '\0' included. That
path was relying on the lexer's old NUL-as-EOF behavior to make ordinary
literal parsing work at all. It now gets its own strlen()-like handling
for char arrays specifically: a single trailing NUL terminator is
excluded, mirroring the pointer overload, while non-char arrays (e.g.
uint8_t buffers for binary formats) are left untouched since a trailing
zero byte there may be data.

Also updates a few existing tests that (mostly incidentally) depended on
a trailing NUL being swallowed - std::array<uint8_t, 5>{"true"} left the
5th element zero-initialized - and adds an FAQ entry.

Signed-off-by: Niels Lohmann <niels.lohmann@gmail.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4RQ1Ahan5YAGbnAQGjZTY
2026-09-15 07:12:35 +00:00

13 KiB

Frequently Asked Questions (FAQ)

Known bugs

Brace initialization yields arrays

!!! question

Why does

```cpp
json j{true};
```

and

```cpp
json j(true);
```

yield different results (`#!json [true]` vs. `#!json true`)?

This is a known issue, and -- even worse -- the behavior differs between GCC and Clang. The "culprit" for this is the library's constructor overloads for initializer lists to allow syntax like

json array = {1, 2, 3, 4};

for arrays and

json object = {{"one", 1}, {"two", 2}}; 

for objects.

!!! tip

To avoid any confusion and ensure portable code, **do not** use brace initialization with the types `basic_json`, `json`, or `ordered_json` unless you want to create an object or array as shown in the examples above.

To explicitly create a single-element array, use `json::array({value})`:

```cpp
json j = json::array({true});  // [true]
```

Opt-in copy semantics (since version 3.12.0)

If you define JSON_BRACE_INIT_COPY_SEMANTICS to 1 before including the library, single-element brace initialization is treated as copy/move instead of creating a single-element array:

#define JSON_BRACE_INIT_COPY_SEMANTICS 1
#include <nlohmann/json.hpp>

json obj = {{"key", "value"}};
json j{obj};   // -> {"key":"value"}  (copy, not array)

Without the macro (default behavior), json j{obj} creates [{"key":"value"}]. This opt-in macro fixes issue #5074 while preserving backwards compatibility for existing code.

Limitations

Relaxed parsing

!!! question

Can you add an option to ignore trailing commas?

This library does not support any feature that would jeopardize interoperability.

Parse errors reading non-ASCII characters

!!! question "Questions"

- Why is the parser complaining about a Chinese character?
- Does the library support Unicode?
- I get an exception `[json.exception.parse_error.101] parse error at line 1, column 53: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '"Testé$')"`

The library supports Unicode input as follows:

  • Only UTF-8 encoded input is supported, which is the default encoding for JSON, according to RFC 8259.
  • std::u16string and std::u32string can be parsed, assuming UTF-16 and UTF-32 encoding, respectively. These encodings are not supported when reading from files or other input containers.
  • Other encodings such as Latin-1 or ISO 8859-1 are not supported and will yield parse or serialization errors.
  • The library will not replace Unicode noncharacters.
  • Invalid surrogates (e.g., incomplete pairs such as \uDEAD) will yield parse errors.
  • The strings stored in the library are UTF-8 encoded. When using the default string type (std::string), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
  • When you store strings with different encodings in the library, calling dump() may throw an exception unless json::error_handler_t::replace or json::error_handler_t::ignore are used as error handlers.

In most cases, the parser is right to complain, because the input is not UTF-8 encoded. This is especially true for Microsoft Windows, where Latin-1 or ISO 8859-1 is often the standard encoding.

NUL bytes in the input

!!! question

Why does parsing fail with "invalid literal" or "unexpected additional data" when my input contains a `'\0'` (NUL) byte?

A '\0' byte that occurs inside or at the end of the input is not treated as end-of-input; it is treated as an ordinary, invalid byte, exactly like any other unexpected byte in that position. RFC 8259 does not give the NUL byte any special end-of-text meaning, so a JSON text that is embedded in a larger byte sequence (for instance, a std::string with a trailing '\0' appended, or a buffer that happens to be zero-padded) will yield a parse_error.101, the same error you would get for any other unexpected trailing or misplaced byte:

  • If the NUL byte follows a complete value, parsing fails with the usual "expected end of input" message the library also gives for any other unexpected trailing byte (e.g., json::parse(std::string("123") + '\0') fails the same way json::parse("123x") does).
  • If the NUL byte occurs where a value is expected (for instance, at the very start of the input, or right after a : or ,), the library reports "invalid literal".
  • A NUL byte inside a quoted string still needs to be escaped as \u0000, as required by RFC 8259; an unescaped NUL there is reported separately as a control character that must be escaped.

Only the length actually passed to the parser matters here: parsing a const char* (for example, a string literal) uses strlen()-like semantics and therefore never sees the terminating NUL, so json::parse("[1,2,3]") is unaffected. What is affected is input that explicitly includes a NUL byte as data, such as a std::string with '\0' appended, or an iterator range/container whose end includes it.

Wide string handling

!!! question

Why are wide strings (e.g., `std::wstring`) dumped as arrays of numbers?

As described above, the library assumes UTF-8 as encoding. To store a wide string, you need to change the encoding.

!!! example

```cpp
#include <codecvt> // codecvt_utf8
#include <locale>  // wstring_convert

// encoding function
std::string to_utf8(std::wstring& wide_string)
{
    static std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
    return utf8_conv.to_bytes(wide_string);
}

json j;
std::wstring ws = L"車B1234 こんにちは";

j["original"] = ws;
j["encoded"] = to_utf8(ws);

std::cout << j << std::endl;
```

The result is:

```json
{
  "encoded": "車B1234 こんにちは",
  "original": [36554, 66, 49, 50, 51, 52, 32, 12371, 12435, 12395, 12385, 12399]
}
```

Usage

Thread safety

!!! question

Is `basic_json` thread-safe?

No. basic_json provides no built-in synchronization, the same as std::map or std::vector. Concurrent reads of the same value from multiple threads are safe, as are concurrent (non-overlapping) accesses to independent json objects. However, any concurrent write to a json object -- or a concurrent read while another thread writes to the same object -- is a data race and requires external synchronization (e.g., a std::mutex) by the caller.

Schema validation

!!! question

Does this library support JSON Schema validation?

Not directly, but the companion project json-schema-validator builds JSON Schema (draft 4, 6, 7, and 2019-09) validation on top of this library and is a common recommendation for this use case.

Exceptions

Parsing without exceptions

!!! question

Is it possible to indicate a parse error without throwing an exception?

Yes, see Parsing and exceptions.

Key name in exceptions

!!! question

Can I get the key of the object item that caused an exception?

Yes, you can. Please define the symbol JSON_DIAGNOSTICS to get extended diagnostics messages.

Serialization issues

Number precision

!!! question

- It seems that precision is lost when serializing a double.
- Can I change the precision for floating-point serialization?

The library uses std::numeric_limits<number_float_t>::digits10 (15 for IEEE doubles) digits for serialization. This value is sufficient to guarantee roundtripping. If one uses more than this number of digits of precision, then string -> value -> string is not guaranteed to round-trip.

!!! quote "cppreference.com"

The value of `std::numeric_limits<T>::digits10` is the number of base-10 digits that can be represented by the type T without change, that is, any number with this many significant decimal digits can be converted to a value of type T and back to decimal form, without change due to rounding or overflow. 

!!! tip

The website https://float.exposed gives a good insight into the internal storage of floating-point numbers.

See this section on the library's number handling for more information.

Serializing untrusted or invalid UTF-8

!!! question "Questions"

- Why does `dump()` throw when I serialize data that came from the network?
- Is CVE-2024-34363 a vulnerability in this library?

Crashes reported against this library that stem from an uncaught type_error.316 while serializing unvalidated input (e.g., CVE-2024-34363) are a usage issue, not a library vulnerability: dump() throws in its default strict mode because RFC 8259 requires JSON text to be valid UTF-8.

The recommended pattern is to pass a non-strict error_handler or to handle the exception:

// replace invalid sequences with U+FFFD instead of throwing
const auto s = j.dump(-1, ' ', false, json::error_handler_t::replace);

Using JSON values with std::format or fmt

!!! question

- Can I use `std::format("{}", j)` on a JSON value?
- Can I use `fmt::format("{}", j)` or `fmt::print("{}", j)` (the [{fmt}](https://github.com/fmtlib/fmt) library) on a JSON value?

std::format works out of the box since version 3.13.0, as long as the standard library provides <format> (see JSON_HAS_STD_FORMAT); see std::formatter<basic_json> for details, including the #!cpp "{:#}" pretty-print spec, indent widths (#!cpp "{:2}"), and custom indent characters (#!cpp "{:.>#}").

For fmt, the library ships format_as, a small customization point fmt looks for via argument-dependent lookup. It only has an effect on fmt 10.0.0 through 11.0.2 — from fmt 11.1.0 onwards, fmt no longer picks up a format_as overload that returns a std::string. On such versions (or any version, if you also want the same #!cpp "{:#}"/width/fill-and-align spec support that std::formatter<basic_json> has), define your own fmt::formatter specialization; see format_as for a recipe that mirrors it.

If you get ambiguous-overload errors when passing a JSON value to fmt::format/fmt::print without any fmt::formatter<json> specialization in scope, that's fmt picking up basic_json's implicit operator ValueType() conversion operator (see #964 and #958); disabling it via JSON_USE_IMPLICIT_CONVERSIONS 0 avoids the ambiguity.

Compilation issues

Android SDK

!!! question

Why does the code not compile with Android SDK?

Android defaults to using very old compilers and C++ libraries. To fix this, add the following to your Application.mk. This will switch to the LLVM C++ library, the Clang compiler, and enable C++11 and other features disabled by default.

APP_STL := c++_shared
NDK_TOOLCHAIN_VERSION := clang3.6
APP_CPPFLAGS += -frtti -fexceptions

The code compiles successfully with Android NDK, Revision 9 - 11 (and possibly later) and CrystaX's Android NDK version 10.

Missing STL function

!!! question "Questions"

- Why do I get a compilation error `'to_string' is not a member of 'std'` (or similarly, for `strtod` or `strtof`)?
- Why does the code not compile with MinGW or Android SDK?

This is not an issue with the code, but rather with the compiler itself. On Android, see above to build with a newer environment. For MinGW, please refer to this site and this discussion for information on how to fix this bug. For Android NDK using APP_STL := gnustl_static, please refer to this discussion.