Skip to content

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 #5404

Description

@nlohmann

Description

The BJData ndarray writer maps _ArrayType_ = "byte" to the type marker B unconditionally, with no gate on bjdata_version. But B (byte) is a Draft-3-only marker: the docs (bjdata.md, "Binary values") state it "must be explicitly enabled using the version parameter of to_bjdata", and BJData Draft 2 has no B type.

In write_bjdata_ndarray (include/nlohmann/detail/output/binary_writer.hpp) the type map contains {"byte", 'B'} and the marker is written regardless of the requested version. So with the default version = draft2, an annotated byte array produces [$B#…, a stream that is invalid under Draft 2 and that a strict third-party Draft-2 reader will reject.

Separately, unlike every other _ArrayType_ (e.g. uint8, which round-trips back to the annotated object), the result parses back as a binary value, so to_bjdata/from_bjdata changes the value.

Low severity / spec-conformance and interop, and only reachable with JData-annotated input, but the behavior contradicts the documented version-gating of B.

Reproduction steps

Serialize a byte ndarray with default (Draft-2) options and inspect the bytes / round-trip.

Expected vs. actual results

  • Expected (Draft-2 default): either a fallback encoding that does not use the Draft-3 B marker, or an error; and round-trip equality like the other dtypes.
  • Actual: bytes contain B (0x42); the value round-trips to a binary value instead of the original object.

Minimal code example

#include <nlohmann/json.hpp>
#include <cstdio>
using json = nlohmann::json;

int main()
{
    json j;
    j["_ArrayType_"] = "byte";
    j["_ArraySize_"] = json::array({2});
    j["_ArrayData_"] = json::array({1, 2});
    auto v = json::to_bjdata(j);              // default draft2
    for (auto b : v) std::printf("%02x ", b); // 5b 24 42 23 5b 69 02 5d 01 02  ("[$B#[i2]..")
    std::printf("\nback=%s\n", json::from_bjdata(v).dump().c_str());
}

Error messages

5b 24 42 23 5b 69 02 5d 01 02
back={"bytes":[1,2],"subtype":null}

(The stream contains the Draft-3 B marker under the Draft-2 default, and the round-trip yields a binary value rather than the original annotated object.)

Suggested fix

Only accept "byte"/emit B when bjdata_version selects Draft 3; otherwise fall back to a plain-object encoding (consistent with the other unrepresentable-annotation cases).

Compiler and operating system

g++ 13.3.0 (Ubuntu 24.04, x86-64)

Library version

develop @ 01853ed6bcf9ebe88ec2e248ea757b86417b1487

Validation

  • The bug also occurs if the latest version from the develop branch is used.
  • I can successfully compile and run the unit tests.

Activity

  1. self-assigned this
    on Sep 5, 2026
  2. added 4 commits that reference this issue on Sep 9, 2026
    daa2e74
    eb45908
    93295e0
    f1e8a90
  3. added this to the Release 3.13.0 milestone on Sep 16, 2026
  4. added a commit that references this issue on Sep 16, 2026
    502e9d6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions