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. These design goals make it different from earlier binary serializations such as ASN.1 and MessagePack.
Turn a binary CBOR sequence of bytes into a nice Elm value.
import Cbor.Decode as D
import Url exposing (Url)
type alias Album =
{ artist : String
, title : String
, year : Int
, tracks : List ( String, Duration )
, links : List Url
}
type Duration
= Duration Int
decodeAlbum : D.Decoder Album
decodeAlbum =
let
link =
D.string
|> D.map Url.fromString
|> D.andThen (Maybe.map D.succeed >> Maybe.withDefault D.fail)
track =
D.pair D.string (D.map Duration D.int)
in
D.map5 Album
D.string
D.string
D.int
(D.list track)
(D.list link)Decode an integer. Note that there's no granular decoders (nor encoders) for integers like 'unsignedInt8' or 'signedInt32' because, the CBOR encoding of an integers varies depending on the integer value. Therefore, at the CBOR-level, as in Elm, there's no notion of smaller integers.
Decode a floating point number, regardless of its precision. Because in Elm, there exists only one 'Float' type, which is encoded in double-precision, we do decode all floats into this double-precision number in Elm which may lead to some precision gain and therefore, different representation than one would expect.
D.decode D.float <| E.encode (E.float16 1.1) == Just 1.099609375
D.decode D.float <| E.encode (E.float64 1.1) == Just 1.1
E.Encode (E.float16 1.1) == Bytes<0xF9, 0x3C, 0x66>
E.Encode (E.float64 1.1) == Bytes<0xFB, 0x3F, 0xF1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A>Decode a bunch UTF-8 bytes into a 'String'. In case of streaming, all strings are decoded at once and concatenated as if they were one big string. See also 'Cbor.Encode.beginStrings'
Decode a bunch bytes into 'Bytes'. In case of streaming, all bytes are decoded at once and concatenated as if they were one big byte string. See also 'Cbor.Encode.beginBytes'
Decode a 'List' of items 'a'. The list can be finite or infinite (see also Streaming in Cbor.Encode).
D.decode (D.list D.int) Bytes<0x82, 0x0E, 0x18, 0x2A> == Just [ 14, 42 ]
D.decode (D.list D.int) Bytes<0x9F, 0x01, 0x02, 0xFF> == Just [ 1, 1 ]
D.decode (D.list (D.list D.bool)) Bytes<0x81, 0x9F, 0xF4, 0xFF> == Just [ [ False ] ]Decode an array or said differently, a list with heterogeneous elements (with different types).
Since Elm (or statically typed languages in general) does not support hetegorenous arrays, we
have to resort to records when parsing. This parser works best with the map
family of functions.
For example, to decode an array with 3 elements:
D.decode (D.array <| D.map3 MyRecord D.bool D.int D.string) (Bytes<0x83 0xF5 0x0E 0x61 0x61>)
== Just (MyRecord True 14 "a")Decode an CBOR map of pairs of data-items into an Elm 'Dict'. The map can be either of finite length or indefinite length (see also Streaming in Cbor.Encode).
D.decode (D.dict D.int D.int) Bytes<0xA1, 0x0E, 0x18, 0x2A>
== Dict.fromList [ ( 14, 42 ) ]
D.decode (D.dict D.string D.int) Bytes<0xBF, 0x61, 0x61, 0x0E, 0x61, 0x62, 0x18, 0x2A, 0xFF>
== Dict.fromList [ ( "a", 14 ), ( "b", 42 ) ]Decode a CBOR (key, value) map, where keys can be potentially arbitrary CBOR.
If keys are comparable, you should prefer dict to this primitive.
Decode a 2-tuple. This is mostly a helper around a list decoder with 2 elements, with non-uniform types.
D.decode (D.pair D.int D.bool) Bytes<0x0E, 0xF5> == Just ( 14, True )Helpful for dealing with optional fields. Turns null into Nothing.
D.decode (D.maybe D.bool) Bytes<0xF6> == Just Nothing
D.decode (D.maybe D.bool) Bytes<0xF4> == Just (Just False)Always succeed to produce a certain Elm value.
D.decode (D.succeed 14) Bytes<0x42, 0x28> == Just 14
D.decode (D.list (D.int |> D.andThen (\_ -> D.succeed 1))) Bytes<0x83, 0x00, 0x00, 0x00>
== Just [ 1, 1, 1 ]This particularly handy when used in combination with andThen.
A decoder that always fail.
D.decode D.fail Bytes<0x00> == NothingThis is particularly handy when used in combination with andThen.
Decode something and then use that information to decode something else. This is useful when a 'Decoder' depends on a value held by another decoder:
tagged : Tag -> Decoder a -> Decoder a
tagged expectedTag decodeA =
tag
|> andThen
(\decodedTag ->
if decodedTag == expectedTag then
decodeA
else
fail
)Transform a decoder.
For example, maybe you just want to know the length of a string:
import String
stringLength : Decoder Int
stringLength =
D.map String.length D.stringTry two decoders and then combine the result. Can be used to decode objects with many fields:
type alias Point =
{ x : Float, y : Float }
point : Decoder Point
point =
D.map2 Point D.float D.floatIt tries each individual decoder and puts the result together with the Point constructor.
Try three decoders and then combine the result. Can be used to decode objects with many fields:
type alias Person =
{ name : String, age : Int, height : Float }
person : Decoder Person
person =
D.map3 Person D.string D.int D.floatLike map2 it tries each decoder in order and then give the results to the
Person constructor. That can be any function though!
Try four decoders and then combine the result. See also 'map3' for some examples.
Try five decoders and then combine the result. See also 'map3' for some examples.
This implementation does little interpretation of the tags and is limited to only decoding the tag's value. The tag's payload has to be specified as any other decoder. So, using the previous example, one could decode a bignum as:
D.decode (D.tag |> D.andThen (\tag -> D.bytes)) inputYou may also use @tagged@ if you as a helper to decode a tagged value, while verifying that the tag matches what you expect.
D.decode (D.tagged PositiveBigNum D.bytes) inputDecode a value that is tagged with the given 'Tag'. Fails if the value is not tagged, or, tag with some other 'Tag'
D.decode (D.tagged Cbor D.int) Bytes<0xD8, 0x0E> == Just ( Cbor, 14 )
D.decode (D.tagged Base64 D.int) Bytes<0xD8, 0x0E> == Nothing
D.decode (D.tagged Cbor D.int) Bytes<0x0E> == NothingDecode remaining bytes as any CborItem. This is useful for debugging
or to inspect some unknown Cbor data.
D.decode D.any <| E.encode (E.int 14) == Just (CborUnsignedInteger 14)
Describes how to turn a binary CBOR sequence of bytes into a nice Elm value.