Parsing CBOR data with numeric keys

For a project, I need to work a lot with CBOR data. This library has been great so far: GitHub - albertorestifo/crystal-cbor: [mirror] CBOR encoding and decoding in Crystal. Basically, it works like YAML and JSON serializer.

But I often run into problems when parsing objects with numeric keys. Here’s an example from the diagnostic:

{1: -8, 4: h'5d9bd15d2b241d42ae767c653e4aaa2ea9e3b2d2da66f0e09d13b9691d12f033', "address": h'01e4740b2690791de5ebad00ea555d8c6c31318ea980999eb7b13a24b955c29e1abea4593c52660b90b6fa20b3e5a42b105666522a3f14af29'}

Ideally, I would like to do something like this:

alias Header = NamedTuple(1: Int32, 4: Bytes, address: Bytes)
header = Header.from_cbor(cbor_string)

But a NamedTuple can’t have numeric keys. So I get around it by parsing the object into a Hash and wrapping the result into a struct to access the properties. This approach works with simple objects like the one above, but it becomes tedious with complex objects involving unions.

So I was wondering, what’s the reason why a NamedTuple can’t have numeric keys? And is there another solution I missed?

My last resort would be forking the library and making @[CBOR::Field(key: 1)] work so I can map the whole object to a Struct. But maybe I’m missing something.

In the meantime, I created a PR on the CBOR library to allow objects with numeric keys to be parsed to structs with CBOR::Field annotations:

struct Headers
  include CBOR::Serializable

  @[CBOR::Field(key: 1)]
  getter one : Int32
  @[CBOR::Field(key: 4)]
  getter four : Bytes
  getter address : Bytes
end

Although, I’m curious to know why a NamedTuple can’t have numeric keys.

NamedTuple mainly exists to support named argument passing, and named arguments can’t have number literals as their names. You have to turn numbers into strings similarly in that case:

def foo(*, 1 x) # Error: unexpected token: "1"
end

foo(1: "a") # Error: expecting token ')', not ':'

def foo(*, "1" x) # okay
end

foo("1": "a") # okay
2 Likes

Right, that makes a lot of sense. Thanks for explaining!