Introduction
The fs2-data-cbor-json
module provides pipes to convert streams between CBOR and JSON representations in both directions.
CBOR to JSON
The library implements decoding of CBOR item streams into JSON token streams.
import fs2._
import fs2.data.cbor
import fs2.data.cbor.low.CborItem
import scodec.bits._
Stream
.emits(List(
CborItem.PositiveInt(hex"cafebabe"),
CborItem.StartArray(3),
CborItem.TextString("Some string"),
CborItem.TextString("Some other string"),
CborItem.False
))
.through(cbor.json.decodeItems[Fallible])
.compile
.toList
// res0: Either[Throwable, List[data.json.Token]] = Right(
// value = List(
// NumberValue(value = "3405691582"),
// StartArray,
// StringValue(value = "Some string"),
// StringValue(value = "Some other string"),
// FalseValue,
// EndArray
// )
// )
CBOR is more expressive than JSON and allows to express more kind of data, so not all CBOR item streams can be successfully converted, or some information loss might occur.
In particular restrictions are:
- maps with non string (text or byte) keys are not supported
- unknown tags are simply ignored
To convert to JSON, some tags are understood by the pipe, namely:
- decoding of byte strings (potentially nested into arrays and maps) into base64 or base16 encoded JSON string (tags 21, 22, and 23)
- decoding of byte strings (potentially nested into arrays and maps) into numbers (tags 2, 3, and 5)
import fs2.data.cbor.Tags
Stream
.emits(List(
CborItem.Tag(Tags.ExpectedBase64Encoding),
CborItem.StartArray(2),
CborItem.ByteString(hex"cafebabe"),
CborItem.ByteString(hex"ff"),
CborItem.Tag(Tags.PositiveBigNum),
CborItem.ByteString(hex"1234567890abcdef")
))
.through(cbor.json.decodeItems[Fallible])
.compile
.toList
// res1: Either[Throwable, List[data.json.Token]] = Right(
// value = List(
// StartArray,
// StringValue(value = "yv66vg=="),
// StringValue(value = "/w=="),
// EndArray,
// NumberValue(value = "1311768467294899695")
// )
// )
JSON to CBOR
Conversely, you can convert a stream of JSON tokens into a stream CBOR items. The pipe tries to encode the elements using the smallest CBOR type that can hold the value. However, some simplications are made:
- all JSON collections (arrays and objects) generate their indefinite length CBOR counterparts
- half-precision floats are never generated
import fs2.data.json
import fs2.data.json.Token
Stream
.emits(List(
Token.StartArray,
Token.StringValue("a string"),
Token.NumberValue("1.34"),
Token.StartObject,
Token.Key("a"),
Token.TrueValue,
Token.Key("b"),
Token.NullValue,
Token.EndObject,
Token.EndArray
))
.through(json.cbor.encodeItems)
.compile
.toList
// res2: cats.package.Id[List[CborItem]] = List(
// StartIndefiniteArray,
// TextString(string = "a string"),
// Tag(tag = 4L),
// StartArray(size = 2L),
// NegativeInt(
// bytes = Chunk(
// bytes = View(
// at = scodec.bits.ByteVector$AtArray@26200c72,
// offset = 0L,
// size = 1L
// )
// )
// ),
// PositiveInt(
// bytes = Chunk(
// bytes = View(
// at = scodec.bits.ByteVector$AtArray@43925234,
// offset = 0L,
// size = 2L
// )
// )
// ),
// StartIndefiniteMap,
// TextString(string = "a"),
// True,
// TextString(string = "b"),
// Null,
// Break,
// Break
// )