Reference
- Parsing
- Writing
- Flavors
- Custom parsers and writers
- Custom Iterators
- Convenience Iterators
- CborReader Helper Procedures
- CborWriter Helper Procedures
- Enums
This page provides an overview of the cbor_serialization API - for details, see the
API reference.
Parsing
Common API
CBOR parsing uses the common serialization API, supporting both object-based and dynamic CBOR data item:
type
  NimServer = object
    name: string
    port: int
  MixedServer = object
    name: CborValueRef
    port: int
  RawServer = object
    name: CborBytes
    port: CborBytes
let rawCbor = Cbor.encode(NimServer(name: "localhost", port: 42))
# decode into native Nim
let native = Cbor.decode(rawCbor, NimServer)
# decode into mixed Nim + CborValueRef
let mixed = Cbor.decode(rawCbor, MixedServer)
# decode any value into nested cbor raw
let raw = Cbor.decode(rawCbor, RawServer)
# decode any valid CBOR, using the `cbor_serialization` node type
let value = Cbor.decode(rawCbor, CborValueRef)
Standalone Reader
A reader can be created from any faststreams-compatible stream:
var reader = Cbor.Reader.init(memoryInput(rawCbor))
let native2 = reader.readValue(NimServer)
# Overwrite an existing instance
var reader2 = Cbor.Reader.init(memoryInput(rawCbor))
var native3: NimServer
reader2.readValue(native3)
Parser options
Parser options allow you to control the limits of the parser. Set them by passing to Cbor.decode or when initializing the reader:
  rawCbor, NimServer, conf = defaultCborReaderConf(nestedDepthLimit: 0))
Flavors can be used to override the defaults for some these options.
Limits
Parser limits are passed to decode, similar to flags:
You can adjust these defaults to suit your needs:
- nestedDepthLimit [=512]: Maximum nesting depth for objects and arrays (0 = unlimited).
- arrayElementsLimit [=0]: Maximum number of array elements (0 = unlimited).
- objectFieldsLimit [=0]: Maximum number of key-value pairs in an object (0 = unlimited).
- stringLengthLimit [=0]: Maximum string length in bytes (0 = unlimited).
- byteStringLengthLimit [=0]: Maximum byte string length in bytes (0 = unlimited).
- bigNumBytesLimit [=64]: Maximum number of BigNum bytes (0 = unlimited).
Special types
- CborBytes: Holds a CBOR value as a distinct seq[byte].
- CborVoid: Skips a valid CBOR value.
- CborNumber: Holds a CBOR number.
- Use toInt(n: CborNumber, T: type SomeInteger): Opt[T]to convert it to an integer.
- The integerfield for negative numbers is set toabs(value)-1as per the CBOR spec. This allows to hold a negativeuint64.highvalue.
 
- Use 
- CborValueRef: Holds any valid CBOR value, it uses CborNumberinstead ofint.
Writing
Common API
Similar to parsing, the common serialization API is used to produce CBOR data items.
# Convert object to cbor raw
let blob = Cbor.encode(native)
Standalone Writer
var output = memoryOutput()
var writer = Cbor.Writer.init(output)
writer.writeValue(native)
let decoded = Cbor.decode(output.getOutput(seq[byte]), NimServer)
echo decoded
Flavors
Flags and limits are runtime configurations, while a flavor is a compile-time mechanism to prevent conflicts between custom serializers for the same type. For example, a CBOR-RPC-based API might require that numbers are formatted as hex strings while the same type exposed through REST should use a number.
Flavors ensure the compiler selects the correct serializer for each subsystem. Use defaultSerialization to assign serializers of a flavor to a specific type.
# Parameters for `createCborFlavor`:
  FlavorName: untyped
  mimeTypeValue = "application/cbor"
  automaticObjectSerialization = false
  automaticPrimitivesSerialization = true
  requireAllFields = true
  omitOptionalFields = true
  allowUnknownFields = true
  skipNullFields = false
type
  OptionalFields = object
    one: Opt[string]
    two: Option[int]
createCborFlavor OptCbor
OptCbor.defaultSerialization OptionalFields
- automaticObjectSerialization: enable automatic serialization for all object types.
- automaticPrimitivesSerialization: enable automatic serialization for all primitive types.
- allowUnknownFields: Skip unknown fields instead of raising an error.
- requireAllFields: Raise an error if any required field is missing.
- omitOptionalFields: Writer ignores fields with null values.
- skipNullFields: Reader ignores fields with null values.
Custom parsers and writers
Parsing and writing can be customized by providing overloads for the readValue and writeValue functions. Overrides are commonly used with a flavor that prevents automatic serialization, to avoid that some types use the default serialization, should an import be forgotten.
# Custom serializers for MyType should match the following signatures
proc readValue*(r: var Cbor.Reader, v: var MyType) {.raises: [IOError, SerializationError].}
proc writeValue*(w: var Cbor.Writer, v: MyType) {.raises: [IOError].}
# When flavors are used, use the flavor reader/writer instead
proc readValue*(r: var MyFlavor.Reader, v: var MyType) {.raises: [IOError, SerializationError].}
proc writeValue*(w: var MyFlavor.Writer, v: MyType) {.raises: [IOError].}
Objects
Decode objects using the parseObject template. To parse values, use helper functions or readValue. The readObject and readObjectFields iterators are also useful for custom object parsers.
proc readValue*(r: var Cbor.Reader, table: var Table[string, int]) =
  parseObject(r, key):
    table[key] = r.parseInt(int)
Sets and List-like Types
Sets and list/array-like structures can be parsed using the parseArray template, which supports both indexed and non-indexed forms.
Built-in readValue implementations exist for regular seq and array. For set or set-like types, you must provide your own implementation.
type
  HoldArray = object
    data: array[3, int]
  HoldSeq = object
    data: seq[int]
  WelderFlag = enum
    TIG
    MIG
    MMA
  Welder = object
    flags: set[WelderFlag]
proc readValue*(r: var Cbor.Reader, value: var HoldArray) =
  # parseArray with index, `i` can be any valid identifier
  r.parseArray(i):
    value.data[i] = r.parseInt(int)
proc readValue*(r: var Cbor.Reader, value: var HoldSeq) =
  # parseArray without index
  r.parseArray:
    let lastPos = value.data.len
    value.data.setLen(lastPos + 1)
    readValue(r, value.data[lastPos])
proc readValue*(r: var Cbor.Reader, value: var Welder) =
  # populating set also okay
  r.parseArray:
    value.flags.incl r.parseInt(int).WelderFlag
Custom Iterators
Custom iterators provide access to sub-token elements:
customIntValueIt(r: var CborReader; body: untyped)
customNumberValueIt(r: var CborReader; body: untyped)
customStringValueIt(r: var CborReader; limit: untyped; body: untyped)
customStringValueIt(r: var CborReader; body: untyped)
Convenience Iterators
readArray(r: var CborReader, ElemType: typedesc): ElemType
readObjectFields(r: var CborReader, KeyType: type): KeyType
readObjectFields(r: var CborReader): string
readObject(r: var CborReader, KeyType: type, ValueType: type): (KeyType, ValueType)
CborReader Helper Procedures
See the API reference
CborWriter Helper Procedures
See the API reference
Enums
type
  Fruit = enum
    Apple = "Apple"
    Banana = "Banana"
  Drawer = enum
    One
    Two
  Number = enum
    Three = 3
    Four = 4
  Mixed = enum
    Six = 6
    Seven = "Seven"
cbor_serialization automatically detects the expected representation for each enum based on its declaration.
- Fruitexpects string literals.
- Drawerand- Numberexpect numeric literals.
- Mixed(with both string and numeric values) is disallowed by default. If the CBOR value does not match the expected style, an exception is raised. You can configure individual enum types:
configureCborDeserialization(
    T: type[enum], allowNumericRepr: static[bool] = false,
    stringNormalizer: static[proc(s: string): string] = strictNormalize)
# Example:
Mixed.configureCborDeserialization(allowNumericRepr = true) # Only at top level
You can also configure enum encoding at the flavor or type level:
type
  EnumRepresentation* = enum
    EnumAsString
    EnumAsNumber
    EnumAsStringifiedNumber
# Examples:
# Flavor level
Cbor.flavorEnumRep(EnumAsString)   # Default flavor, can be called from non-top level
Flavor.flavorEnumRep(EnumAsNumber) # Custom flavor, can be called from non-top level
# Individual enum type, regardless of flavor
Fruit.configureCborSerialization(EnumAsNumber) # Only at top level
# Individual enum type for a specific flavor
MyCbor.flavorEnumRep(Drawer, EnumAsString) # Only at top level