From 78821cd9c2af4c802f256a7eacfa223279ce044e Mon Sep 17 00:00:00 2001 From: KBS Date: Tue, 4 Aug 2026 02:15:53 +0900 Subject: [PATCH] docs: document standards compliance and parse() vs operator>> strictness (#5326) * docs: document RFC 8259 / JSONTestSuite compliance and parse() vs operator>> strictness The compliance story lived only in tests/src/unit-testsuites.cpp, so drive-by comparisons kept claiming the library "does not fully pass JSONTestSuite". Make it discoverable: - README: add a "Standards compliance" note stating that both nst JSONTestSuite revisions run in CI, that all mandatory y_/n_ cases pass through the strict parse() entry point, and listing the deliberate implementation-defined i_ choices (unbounded nesting, silent BOM stripping, noncharacters forwarded, strict rejection of invalid UTF-8 and lone surrogates, out_of_range.406 on numeric overflow). - features/parsing: add a "Strictness and trailing data" section documenting that parse() is strict and rejects trailing data while operator>> follows relaxed iostream semantics (parses one value and leaves the stream positioned after it) -- the single place a naive test yields a "non-compliant" result. Documentation only; no parser behavior change. Closes #5290. Signed-off-by: manon * docs: correct test-data vendoring and parse()/operator>> claims per review - README: the JSONTestSuite data is downloaded from nlohmann/json_test_data at configure time, not vendored/committed; say so. - README: only the updated suite runs y_ and n_ cases through strict parse(); the original suite's y_ cases go through operator>>. Narrow the claim. - parsing/index.md and operator_gtgt.md: note that operator>> consumes a number's terminating byte, so concatenated numbers must be whitespace-separated (1 2 works, 1true does not); structural and literal values are unaffected. Signed-off-by: manon --------- Signed-off-by: manon Co-authored-by: manon --- README.md | 9 +++++++++ docs/mkdocs/docs/features/parsing/index.md | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/README.md b/README.md index 31e80e908..c63c512f4 100644 --- a/README.md +++ b/README.md @@ -1822,6 +1822,15 @@ The library itself consists of a single header file licensed under the MIT licen ## Notes +### Standards compliance + +The library targets strict conformance with [RFC 8259](https://tools.ietf.org/html/rfc8259.html). Both the original [JSONTestSuite](https://github.com/nst/JSONTestSuite) and its updated revision are exercised in CI; their test data is downloaded from [`nlohmann/json_test_data`](https://github.com/nlohmann/json_test_data) at configure time rather than committed to this repository (see [`tests/src/unit-testsuites.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-testsuites.cpp)): + +- The updated revision runs all mandatory `y_` (must-accept) and `n_` (must-reject) cases through the strict [`parse()`](https://json.nlohmann.me/api/basic_json/parse/) entry point; the original suite runs its `n_` cases through `parse()` and its `y_` cases through [`operator>>`](https://json.nlohmann.me/api/operator_gtgt/). +- The `i_` (implementation-defined) cases are, by RFC 8259, free to be accepted *or* rejected, so "passing all `i_` cases" is not a meaningful conformance metric. The library makes deliberate, documented choices there: nesting depth is not artificially limited, a leading UTF-8 byte order mark is silently ignored, [Unicode noncharacters](https://www.unicode.org/faq/private_use.html#nonchar1) are forwarded unchanged, invalid UTF-8 and lone/unpaired UTF-16 surrogates are rejected (stricter than required), and a number that cannot be stored without becoming `NaN`/`INF` raises [`out_of_range.406`](https://json.nlohmann.me/home/exceptions/#jsonexceptionout_of_range406). + +One behavioral nuance is worth calling out, because a superficial test often misreads it as non-compliance: [`parse()`](https://json.nlohmann.me/api/basic_json/parse/) is strict and rejects trailing data after a value, whereas [`operator>>`](https://json.nlohmann.me/api/operator_gtgt/) follows relaxed iostream semantics — it parses a single value and leaves the stream positioned right after it. Feeding "a valid document followed by trailing bytes" through `operator>>` reports success; the same input through `parse()` is rejected. This is a documented two-API design, not a conformance gap. See [**parsing**](https://json.nlohmann.me/features/parsing/) for details. + ### Character encoding The library supports **Unicode input** as follows: diff --git a/docs/mkdocs/docs/features/parsing/index.md b/docs/mkdocs/docs/features/parsing/index.md index c3cb88f63..17624b8a2 100644 --- a/docs/mkdocs/docs/features/parsing/index.md +++ b/docs/mkdocs/docs/features/parsing/index.md @@ -28,6 +28,22 @@ Inputs consisting of multiple values separated by newlines are handled by the [J By default, the library rejects comments and trailing commas. Both can be enabled with parameters of the `parse` function — see [comments](../comments.md) and [trailing commas](../trailing_commas.md). +## Strictness and trailing data + +[`parse`](../../api/basic_json/parse.md) reads a single JSON value and requires the whole input to be consumed: any +non-whitespace data after the value is reported as a parse error. Use it when you want to guarantee that an input is +exactly one complete JSON document. + +[`operator>>`](../../api/operator_gtgt.md) follows relaxed `#!cpp std::istream` semantics instead: it parses one JSON +value and leaves the stream positioned right after it, without requiring the rest of the stream to be consumed. This is +what makes it possible to read several concatenated values from the same stream, but it also means that "a valid +document followed by trailing bytes" is accepted rather than rejected. If you are validating conformance, or need to +reject any input that is not exactly one JSON document, prefer `parse`. + +When using `operator>>` to read several concatenated values this way, a value that is a number must be followed by +whitespace, because `operator>>` consumes the character that terminates a number — see the +[`operator>>` notes](../../api/operator_gtgt.md#notes) for details and examples. + ## SAX vs. DOM parsing The library offers two parsing models: