Skip to content

Fix to_bjdata() emitting the Draft-3-only 'B' marker in default Draft-2 mode - #5479

Merged
nlohmann merged 2 commits into
developfrom
issue-5404-bjdata-byte-draft-gate
Sep 16, 2026
Merged

nlohmann merged 2 commits into
developfrom
issue-5404-bjdata-byte-draft-gate

Conversation

@nlohmann

@nlohmann nlohmann commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Stacked PR

This PR is stacked on top of #5473 (fix for #5403) and is based on its branch issue-5403-bjdata-uint-range rather than develop. It should be reviewed and merged after that PR.

Summary

In the same BJData ndarray writer touched by #5473, _ArrayType_ = "byte" mapped unconditionally to the BJData type marker 'B', regardless of the bjdata_version requested for to_bjdata(). But 'B' is defined only by BJData Draft 3; the docs (docs/mkdocs/docs/features/binary_formats/bjdata.md, "Binary values") say the Draft 3 optimized binary array "must be explicitly enabled using the version parameter of to_bjdata".

With the default version = draft2, this produced a stream containing a marker that Draft 2 does not define and, unlike every other _ArrayType_, the value round-tripped back through from_bjdata as a binary value instead of the original annotated object — because a 'B'-typed optimized array without the ndarray flag is otherwise the library's own encoding for binary data.

json j;
j["_ArrayType_"] = "byte";
j["_ArraySize_"] = json::array({2});
j["_ArrayData_"] = json::array({1, 2});
auto v = json::to_bjdata(j);  // default draft2 — used to emit the 'B' marker anyway
// from_bjdata(v) used to give a binary value, not the original object

Fix

write_bjdata_ndarray now checks bjdata_version right after resolving dtype from _ArrayType_: if dtype == 'B' and bjdata_version is not draft3, it falls back to the same plain-object encoding already used by this function for other invalid-annotation cases (the same mechanism added/reused in #5473). Draft 3 explicitly selected keeps emitting the compact 'B' ndarray encoding exactly as before.

Tests

  • Updated the existing round-trip check for v_B in the "optimized ndarray (type and vector-size ndarray with JData annotations)" section to pass bjdata_version_t::draft3 explicitly, since v_B uses the Draft-3-only 'B' marker.
  • Split "byte" out of the generic type loop in "ndarray parsed from text is written as a typed array" into its own check that explicitly selects Draft 3.
  • Added a new section, "ndarray with _ArrayType_ "byte" is gated by the BJData draft version", covering:
    • default (Draft 2) falls back to a plain object and round-trips
    • explicit Draft 2 behaves the same as the default
    • explicit Draft 3 still uses the compact 'B' ndarray encoding, with an exact expected byte sequence, and round-trips correctly

Ran the full unit-bjdata suite offline (compiled against include/ with a stub test_data.hpp): 693941/693942 assertions pass; the one failure and the one skipped test case are pre-existing and unrelated (they require downloaded test data, unavailable in this offline setup) — identical to the baseline on develop. Also ran unit-ubjson, unit-binary_formats, and unit-regression2 offline with no new failures.

Breaking change?

No breaking changes to the public API. This changes only the wire output of to_bjdata() for annotated objects with _ArrayType_ = "byte" under the default (Draft 2) mode: such objects now serialize as plain objects (matching every other invalid-for-the-current-mode annotation already handled by this function) instead of emitting a Draft-3-only marker that Draft 2 readers do not define. Draft 3 output is unchanged.

Fixes #5404.

— opened by Claude Code on behalf of @nlohmann

@nlohmann nlohmann added the review needed It would be great if someone could review the proposed changes. label Sep 5, 2026
@nlohmann nlohmann self-assigned this Sep 5, 2026
@nlohmann
nlohmann force-pushed the issue-5404-bjdata-byte-draft-gate branch from 50e392a to daa2e74 Compare September 9, 2026 08:21
Base automatically changed from issue-5403-bjdata-uint-range to develop September 10, 2026 15:59
@nlohmann
nlohmann force-pushed the issue-5404-bjdata-byte-draft-gate branch 2 times, most recently from eb45908 to 93295e0 Compare September 11, 2026 15:56
// readers reject, so such an object falls back to a plain object
// encoding instead (see the "Binary values" section of the BJData
// documentation)
if (dtype == 'B' && bjdata_version != bjdata_version_t::draft3)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to future-proof this to "at least draft3"?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point — bjdata_version_t only has two values today, so != and < happened to coincide. Pushed a change to bjdata_version < bjdata_version_t::draft3, which keeps working if a later draft keeps the 'B' marker valid.

Written by Claude Code on behalf of @nlohmann.

…-2 mode

_ArrayType_ = "byte" mapped unconditionally to the BJData type marker
'B', regardless of the requested bjdata_version. 'B' is defined only by
BJData Draft 3; with the default version (draft2), this produced a
stream that is invalid for Draft 2 and, unlike every other
_ArrayType_, round-tripped back as a binary value instead of the
original annotated object.

Only accept "byte" / emit 'B' when bjdata_version selects Draft 3.
Under Draft 2, fall back to the same plain-object encoding used
elsewhere in this function for other invalid-annotation cases, so the
value round-trips correctly.

Fixes #5404.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@gregmarr pointed out that dtype == 'B' && bjdata_version != draft3
only future-proofs by accident, since bjdata_version_t currently has
exactly two values. Compare with < instead, so a later draft that
keeps the 'B' marker valid does not need this gate revisited.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann
nlohmann force-pushed the issue-5404-bjdata-byte-draft-gate branch from 9b19e94 to b205250 Compare September 16, 2026 18:17
@nlohmann nlohmann added this to the Release 3.13.0 milestone Sep 16, 2026
@nlohmann
nlohmann merged commit 502e9d6 into develop Sep 16, 2026
22 of 158 checks passed
@nlohmann
nlohmann deleted the issue-5404-bjdata-byte-draft-gate branch September 16, 2026 18:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

M 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.

to_bjdata() emits the Draft-3-only 'B' (byte) marker for _ArrayType_:"byte" even in default Draft-2 mode, and the value does not round-trip

2 participants