Skip to content

# CBOR

The Concise Binary Object Representation (CBOR) is a data format whose design goals include the possibility of extremely small code size, fairly small message size, and extensibility without the need for version negotiation.

References

- CBOR Website - the main source on CBOR
- CBOR Playground - an interactive webpage to translate between JSON and CBOR
- RFC 7049 - the CBOR specification

## Serialization

The library uses the following mapping from JSON values types to CBOR types according to the CBOR specification (RFC 7049):

JSON value type | value/range | CBOR type | first byte
--- | --- | --- | ---
null | `null` | Null | 0xF6
boolean | `true` | True | 0xF5
boolean | `false` | False | 0xF4
number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B
number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A
number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39
number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38
number_integer | -24..-1 | Negative integer | 0x20..0x37
number_integer | 0..23 | Integer | 0x00..0x17
number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18
number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19
number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A
number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B
number_unsigned | 0..23 | Integer | 0x00..0x17
number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18
number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19
number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A
number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B
number_float | any value representable by a float | Single-Precision Float | 0xFA
number_float | any value NOT representable by a float | Double-Precision Float | 0xFB
string | length: 0..23 | UTF-8 string | 0x60..0x77
string | length: 23..255 | UTF-8 string (1 byte follow) | 0x78
string | length: 256..65535 | UTF-8 string (2 bytes follow) | 0x79
string | length: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A
string | length: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B
array | size: 0..23 | array | 0x80..0x97
array | size: 23..255 | array (1 byte follow) | 0x98
array | size: 256..65535 | array (2 bytes follow) | 0x99
array | size: 65536..4294967295 | array (4 bytes follow) | 0x9A
array | size: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B
object | size: 0..23 | map | 0xA0..0xB7
object | size: 23..255 | map (1 byte follow) | 0xB8
object | size: 256..65535 | map (2 bytes follow) | 0xB9
object | size: 65536..4294967295 | map (4 bytes follow) | 0xBA
object | size: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB
binary | size: 0..23 | byte string | 0x40..0x57
binary | size: 23..255 | byte string (1 byte follow) | 0x58
binary | size: 256..65535 | byte string (2 bytes follow) | 0x59
binary | size: 65536..4294967295 | byte string (4 bytes follow) | 0x5A
binary | size: 4294967296..18446744073709551615 | byte string (8 bytes follow) | 0x5B

Binary values with subtype are mapped to tagged values (0xD8..0xDB) depending on the subtype, followed by a byte string, see "binary" cells in the table above.

Complete mapping

The mapping is complete in the sense that any JSON value type can be converted to a CBOR value.

NaN/infinity handling

If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the normal JSON serialization which serializes NaN or Infinity to `null`.

Unused CBOR types

The following CBOR types are not used in the conversion:

- UTF-8 strings terminated by "break" (0x7F)
- arrays terminated by "break" (0x9F)
- maps terminated by "break" (0xBF)
- byte strings terminated by "break" (0x5F)
- date/time (0xC0..0xC1)
- bignum (0xC2..0xC3)
- decimal fraction (0xC4)
- bigfloat (0xC5)
- expected conversions (0xD5..0xD7)
- simple values (0xE0..0xF3, 0xF8)
- undefined (0xF7)
- half-precision floats (0xF9)
- break (0xFF)

Tagged items

Binary subtypes will be serialized as tagged items. See binary values for an example.

Example

    #include <iostream>
    #include <iomanip>
    #include <nlohmann/json.hpp>

    using json = nlohmann::json;
    using namespace nlohmann::literals;

    int main()
    {
        // create a JSON value
        json j = R"({"compact": true, "schema": 0})"_json;

        // serialize it to CBOR
        std::vector<std::uint8_t> v = json::to_cbor(j);

        // print the vector content
        for (auto& byte : v)
        {
            std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)byte << " ";
        }
        std::cout << std::endl;
    }

Output:

    0xa2 0x67 0x63 0x6f 0x6d 0x70 0x61 0x63 0x74 0xf5 0x66 0x73 0x63 0x68 0x65 0x6d 0x61 0x00

## Deserialization

The library maps CBOR types to JSON value types as follows:

CBOR type | JSON value type | first byte
--- | --- | ---
Integer | number_unsigned | 0x00..0x17
Unsigned integer | number_unsigned | 0x18
Unsigned integer | number_unsigned | 0x19
Unsigned integer | number_unsigned | 0x1A
Unsigned integer | number_unsigned | 0x1B
Negative integer | number_integer | 0x20..0x37
Negative integer | number_integer | 0x38
Negative integer | number_integer | 0x39
Negative integer | number_integer | 0x3A
Negative integer | number_integer | 0x3B
Byte string | binary | 0x40..0x57
Byte string | binary | 0x58
Byte string | binary | 0x59
Byte string | binary | 0x5A
Byte string | binary | 0x5B
UTF-8 string | string | 0x60..0x77
UTF-8 string | string | 0x78
UTF-8 string | string | 0x79
UTF-8 string | string | 0x7A
UTF-8 string | string | 0x7B
UTF-8 string | string | 0x7F
array | array | 0x80..0x97
array | array | 0x98
array | array | 0x99
array | array | 0x9A
array | array | 0x9B
array | array | 0x9F
map | object | 0xA0..0xB7
map | object | 0xB8
map | object | 0xB9
map | object | 0xBA
map | object | 0xBB
map | object | 0xBF
False | `false` | 0xF4
True | `true` | 0xF5
Null | `null` | 0xF6
Half-Precision Float | number_float | 0xF9
Single-Precision Float | number_float | 0xFA
Double-Precision Float | number_float | 0xFB

Incomplete mapping

The mapping is incomplete in the sense that not all CBOR types can be converted to a JSON value. The following CBOR types are not supported and will yield parse errors:

- date/time (0xC0..0xC1)
- bignum (0xC2..0xC3)
- decimal fraction (0xC4)
- bigfloat (0xC5)
- expected conversions (0xD5..0xD7)
- simple values (0xE0..0xF3, 0xF8)
- undefined (0xF7)

Object keys

CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected.

Tagged items

Tagged items will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`. They can be stored by passing `cbor_tag_handler_t::store` to function `from_cbor`.

Example

    #include <iostream>
    #include <iomanip>
    #include <nlohmann/json.hpp>

    using json = nlohmann::json;

    int main()
    {
        // create byte vector
        std::vector<std::uint8_t> v = {0xa2, 0x67, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63,
                                       0x74, 0xf5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6d,
                                       0x61, 0x00
                                      };

        // deserialize it with CBOR
        json j = json::from_cbor(v);

        // print the deserialized JSON value
        std::cout << std::setw(2) << j << std::endl;
    }

Output:

    {
      "compact": true,
      "schema": 0
    }

Last update: May 1, 2022
Back to top