Skip to content

Add test coverage for ordered_json/alt_json across binary formats and patch/diff/flatten APIs - #5480

Open
nlohmann wants to merge 2 commits into
developfrom
issue-5421-ordered-json-coverage
Open

Add test coverage for ordered_json/alt_json across binary formats and patch/diff/flatten APIs#5480
nlohmann wants to merge 2 commits into
developfrom
issue-5421-ordered-json-coverage

Conversation

@nlohmann

@nlohmann nlohmann commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #5421. tests/src/unit-ordered_json.cpp is only 83 lines and mostly checks basic insertion order; the rest of the test suite exercises ordered_json almost nowhere. A repo-wide grep for ::(to|from)_(cbor|msgpack|ubjson|bson|bjdata) shows ~2000 hits for json::* and zero for ordered_json. The alt_json custom-string_t specialization from tests/src/unit-alt-string.cpp was likewise never exercised through the binary formats.

This PR adds a new test file, tests/src/unit-ordered_json2.cpp (picked up automatically by the file(GLOB ... src/unit-*.cpp) in tests/CMakeLists.txt — no build-file changes needed), plus a small addition to the existing tests/src/unit-std-format.cpp.

What's covered

  • Binary format round trips for both ordered_json and alt_json, across CBOR, MessagePack, UBJSON, BSON, and BJData: a representative nested value (object + array + nested object + bool/null/number/string), with keys inserted in non-alphabetical order.
    • For ordered_json, in addition to == equality, the actual iteration order of keys (top-level and nested) is compared explicitly before/after the round trip, since preserving that order is the whole point of ordered_json.
    • alt_json needed two additions to the alt_string helper (duplicated locally in the new file rather than shared, since every unit-*.cpp compiles into its own standalone test binary): a std::string constructor and a find(char, pos) overload. Without them, to_bson/from_ubjson fail to compile — presumably why this gap existed, since unit-alt-string.cpp never exercises the binary readers/writers.
  • A characterization test pinning down that ordered_json::operator== is order-sensitive (unlike json, whose object_t is std::map): ordered_map doesn't define its own operator==, so it inherits std::vector's element-wise comparison. This is worth a maintainer's attention as a documented/intentional behavior difference — see "Surprises" below.
  • A duplicate-key binary-decode test: a hand-built CBOR map with a repeated key decoded through both json and ordered_json. Both end up with a single entry holding the last value (via operator[]), which is worth contrasting with the first-value-wins behavior of the initializer-list construction path already pinned in unit-ordered_json.cpp.
  • flatten()/unflatten() round trip on ordered_json, including a top-level and nested key-order check.
  • diff()/patch()/patch_inplace() round trip on ordered_json (replace + remove + add).
  • merge_patch() on ordered_json, including a key-order check after a patch that removes and adds keys.
  • std::formatter<ordered_json> instantiation check added to unit-std-format.cpp (guarded by the same #if JSON_HAS_STD_FORMAT), confirming the formatter — written against the generic NLOHMANN_BASIC_JSON_TPL_DECLARATION — actually instantiates and works for a non-default basic_json template argument. This mirrors the precedent set by unit-format-as.cpp's ADL-deduction test for format_as().

Surprises / findings worth flagging

  1. ordered_json::operator== is order-sensitive. Two ordered_json objects with identical key/value pairs inserted in different order compare unequal, because ordered_map has no custom operator== and inherits std::vector's element-wise comparison. json's operator== (via std::map) never has this issue. This isn't necessarily a bug, but it's a sharp edge worth documenting explicitly if it isn't already, since it's easy to assume ordered_json equality behaves like json's aside from dump() output order.
  2. alt_string (in unit-alt-string.cpp) doesn't compile against the binary writers/readers as-is — it's missing a std::string constructor (needed by the UBJSON/BSON high-precision-number SAX path) and a find(char, pos) overload (needed by BSON's embedded-NUL check on string keys). I worked around this locally in the new file rather than touching unit-alt-string.cpp, to keep this PR strictly additive/test-only. Might be worth adding those two members to the canonical alt_string in a follow-up so other tests can exercise it against the binary formats too.
  3. Duplicate-key handling for binary decode is consistent between json and ordered_json (last value wins, single entry) but differs from the initializer-list construction path (first value wins) — already implicitly documented in unit-ordered_json.cpp, now explicitly cross-referenced.

Left out of scope

Per the issue's own list of stretch goals, the following were intentionally left out to keep this PR focused:

  • alt_json non-binary APIs (update(), merge_patch(), unflatten(), emplace/emplace_back, std::hash, three-way comparison).
  • Custom-allocator basic_json (from unit-allocator.cpp) through the binary readers.

Does this PR introduce a breaking change?

No. This PR only adds tests (tests/src/unit-ordered_json2.cpp is new, and tests/src/unit-std-format.cpp gains one additional guarded TEST_CASE). No files under include/ were touched, so make amalgamate was not needed and the single-header/public API is unaffected.

— opened by Claude Code on behalf of @nlohmann

… patch/diff/flatten APIs

Closes a test-coverage gap from #5421: ordered_json (and the alt_string-based
basic_json specialization from unit-alt-string.cpp) were never round-tripped
through the binary formats (CBOR/MessagePack/UBJSON/BSON/BJData), nor through
flatten()/unflatten(), diff()/patch()/patch_inplace(), or merge_patch(). Also
adds a std::formatter<ordered_json> spot-check, mirroring the precedent set
by the format_as() ADL-deduction test.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann nlohmann added the review needed It would be great if someone could review the proposed changes. label Sep 5, 2026
…dy modernize-pass-by-value)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L review needed It would be great if someone could review the proposed changes. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing test coverage: ordered_json — binary formats, patch/diff, flatten/unflatten, and other non-default basic_json specializations

1 participant