Skip to main content

JSON vs CBOR

JSON and CBOR represent the same data model – numbers, strings, booleans, null, arrays, and maps – but with fundamentally different encodings. JSON encodes everything as human-readable text, paying a size and parsing cost for readability. CBOR encodes data in a compact binary format where each item carries a 3-bit major type and 5-bit additional info in its first byte, eliminating field-name repetition overhead for well-known structures. CBOR is defined by RFC 8949 (December 2020, STD 94) and was specifically designed to be a binary JSON with a much smaller code footprint than Protobuf – suitable for embedded systems and IoT devices with 8-bit microcontrollers. The FIDO Alliance chose CBOR as the mandatory encoding for WebAuthn authenticator data, CTAP2 commands, and COSE (CBOR Object Signing and Encryption, RFC 8152).

JSON is a human-readable text format – universally supported, self-describing, easy to debug. CBOR (Concise Binary Object Representation) is the binary equivalent defined by RFC 8949. CBOR produces smaller payloads than equivalent JSON for typical API structures by eliminating string key repetition and encoding integers natively – the actual reduction depends on payload shape. CBOR parses faster with no string processing and supports 64-bit integers and byte strings natively. CBOR is the wire format for FIDO2/WebAuthn, CoAP, and constrained IoT devices.

FeatureJSONCBOR
EncodingText (UTF-8)Binary (RFC 8949 initial byte + payload)
Human readableYes – readable without toolsNo – requires cbor2, python-cbor, or hex dump
RFCRFC 8259 (2017), ECMA-404RFC 8949 (2020), STD 94
Payload sizeBaselineSmaller for typical API payloads – eliminates string key overhead and encodes integers natively. Actual reduction depends on payload structure: key-heavy objects benefit most, string-value-heavy payloads less so.
IntegersIEEE 754 double – max safe 2^53-1Native uint64/int64 – full 64-bit range without loss
Binary dataBase64 encode required (+33% size)Native byte string (major type 2) – no encoding overhead
Schema requiredNo – self-describingNo – self-describing with major type tags
Type system6 types: string, number, boolean, null, array, object8 major types + semantic tags for dates, UUIDs, bignums
StreamingNewline-delimited JSON or SSEIndefinite-length sequences for streaming (§3.2.3)
Canonical formNo standard – key order not guaranteedCTAP2 Canonical CBOR defined for deterministic encoding
Browser supportJSON.parse() native in all browsersNo native support – cbor-js or similar required
Primary use casesREST APIs, config files, web data interchangeFIDO2/WebAuthn, CoAP (IoT), COSE, constrained devices

When to use JSON

JSON is correct for: public REST APIs, browser-facing applications, any context where human readability and universal tooling matter. If you need third parties to consume your API without special libraries, if your payload contains mostly string data, or if debugging with curl/browser devtools is important – use JSON.

When to use CBOR

CBOR is correct for: FIDO2/WebAuthn implementations (mandatory by spec), CoAP-based IoT systems, applications that transmit binary data (certificates, keys, sensor readings) frequently, and any system on constrained hardware where JSON parsing overhead or base64 encoding costs are significant. CBOR is also the right choice when 64-bit integer precision is required without workarounds.

Common Mistakes

  • Using JSON to transmit binary data like cryptographic keys or image thumbnails – base64 adds 33% overhead and parsing cost. CBOR byte strings eliminate both.
  • Assuming CBOR requires a schema like Protobuf – CBOR is schema-free and self-describing. The major type in each initial byte identifies the value type without any external definition.
  • Using JSON integers for FIDO2/WebAuthn credential IDs – the WebAuthn spec mandates CBOR encoding for authenticator data. Trying to use JSON here breaks compliance.
  • Expecting universal CBOR tooling at the same level as JSON – CBOR library support is good (Python cbor2, Go github.com/fxamacker/cbor, Rust ciborium) but not as ubiquitous as JSON in every framework.

FAQ

Is CBOR a strict binary version of JSON?

CBOR supports the same six JSON value types (string, number, boolean, null, array, map) and adds native 64-bit integers, byte strings, semantic tags, and floating-point half-precision. Every JSON document can be losslessly converted to CBOR. Not every CBOR document can be converted to JSON – byte strings and 64-bit integers have no direct JSON equivalent.

Why did FIDO2 and WebAuthn choose CBOR over JSON?

WebAuthn authenticator data includes raw binary fields (credential IDs, public keys, attestation certificates) that are awkward in JSON. CBOR handles binary natively without base64. CBOR also has a well-defined canonical form (CTAP2 Canonical CBOR) required for deterministic signing. JSON has no canonical form standard, making it unsuitable for cryptographic applications where byte-identical encoding is required.

How much smaller is CBOR than JSON in practice?

For typical API payloads with string-heavy data, CBOR is 15–30% smaller. For payloads with binary data that would require base64 in JSON, CBOR can be 40–60% smaller. For integer-heavy payloads (sensor data, telemetry), the savings depend on value magnitude – small integers (0–23) encode in a single byte in CBOR.