Build json decoders and encoders simultaneously, conserving the need for functions that do each in simple cases. This allows a single datum to embody both the encoder and the decoder for a type, reducing some duplication and redundancy. In simple cases, a codec can be built exclusively by composing other codecs.
In more complex cases, fold could be used, which allows fields to be
encoded and extracted separately.
To just specify a decoder and encoder separately, use init.
module Session exposing (..)
import JsonCodec exposing (Codec)
import Dict exposing (Dict)
import Json.Decode as JD
import Json.Encode as JE
type alias Session =
{ queue : List String
, playing : Maybe (String, Float)
, likeCategories : Dict String Int
}
type Alternative = A | Unknown String
-- An example showing the use of ```map``` to augment a
-- bijective encoding.
altcoder : Codec Alternative
altcoder =
JsonCodec.string
|> JsonCodec.map
(\a -> if a == "A" then A else Unknown a)
(\v ->
case v of
A -> "A"
Unknown x -> x
)
type Picky = OptA | OptB
pickyOpts : Dict String Picky
pickyOpts = Dict.fromList [("A", OptA), ("B", OptB)]
-- An example showing the use of ```andThen``` to exclude
-- bad values.
pickycoder : Codec Picky
pickycoder =
JsonCodec.string |> JsonCodec.andThen
(((flip Dict.get) pickyOpts)
>> Maybe.map JD.succeed
>> Maybe.withDefault (JD.fail "Option must be A or B")
)
(\p -> pickyOpts
|> Dict.toList
|> List.filterMap (\(k,v) -> if v == p then Just k else Nothing)
|> List.head
|> Maybe.withDefault "A"
)
-- A codec that can stand on its own and also be reused.
playingSerializer : Codec (Maybe (String,Float))
playingSerializer =
JsonCodec.nullable
(JsonCodec.object2
(,)
identity
("u", JsonCodec.string)
("p", JsonCodec.float)
)
-- Simple codec built with composition.
serializer : Codec Session
serializer =
JsonCodec.object3
Session
(\s -> (s.queue,s.playing,s.likeCategories))
("queue", JsonCodec.list JsonCodec.string)
("playing", playingSerializer)
("like", JsonCodec.dict JsonCodec.int)
-- An example of using fold to create the same codec.
foldcoder : Codec Session
foldcoder =
let setQueue q s = { s | queue = q } in
let setPlaying p s = { s | playing = p } in
let setLikeCats l s = { s | likeCategories = l } in
JsonCodec.fold
[ JsonCodec.foldField "queue"
(setQueue, JD.list JD.string)
(.queue >> List.map JE.string >> JE.list)
, JsonCodec.foldField "playing"
(setPlaying, JsonCodec.decoder playingSerializer)
(.playing >> JsonCodec.encoder playingSerializer)
, JsonCodec.foldField "like"
(setLikeCats, JD.dict JD.int)
(.likeCategories
>> Dict.map (always JE.int) >> Dict.toList >> JE.object
)
]
{ queue = [], playing = Nothing, likeCategories = Dict.empty }Codec matching and producing strings.
Codec matching and producing bools.
Codec matching and producing ints.
Codec matching and producing floats.
Codec that maps null to Nothing and vice versa.
Codec that produces and consumes lists.
Codec that produces and consumes arrays.
Codec that produces and consumes dictionaries of other values.
Codec that produces and consumes key value pair lists of other values.
Codec that matches a single field similar to Json.Decode and produces a singleton object with 1 field.
Codec that matches and produces objects with 2 given named fields.
Codec that matches and produces objects with 3 given named fields.
Codec that matches and produces objects with 4 given named fields.
Codec that matches and produces objects with 5 given named fields.
Codec that matches and produces objects with 6 given named fields.
Codec that matches and produces objects with 7 given named fields.
Codec that matches and produces objects with 8 given named fields.
Codec that matches null, produces null.
Codec that produces a constant decoded value and encodes to a constant value. One might use this to check a field with a constant value, such as a version number.
Codec that produces a constant encoded value but always fails decoding. One might use this while prefiltering inputs based on their structure but ensure that encoded json has the right structure.
Wrap the codec in a transformer that produces and consumes another type.
type Alternative = A | Unknown String
altcoder : Codec Alternative
altcoder =
Codec.string
|> Codec.map
(\a -> if a == "A" then A else Unknown a)
(\v ->
case v of
A -> "A"
Unknown x -> x
)Like map, but the decode function returns a decoder that will be evaluated next, rather than just mapping the value.
-- An example showing the use of ```andThen``` to exclude
-- bad values.
pickycoder : Codec Picky
pickycoder =
JsonCodec.string |> JsonCodec.andThen
(((flip Dict.get) pickyOpts)
>> Maybe.map JD.succeed
>> Maybe.withDefault (JD.fail "Option must be A or B")
)
(\p -> pickyOpts
|> Dict.toList
|> List.filterMap (\(k,v) -> if v == p then Just k else Nothing)
|> List.head
|> Maybe.withDefault "A"
)Match one of many decoders, encode using the given function.
Get a Json.Decode.Decoder from a codec.
Get a function that encodes Json.Encode.Value from a codec.
Construct an arbitrary codec from a decoder and an encoder function.
Produce a codec that uses a list of fields and codecs to fold new values into an object, producing a new object with the json loaded on to it.
This can be used to consume delta updates from a channel, create codecs that operate on objects with many fields, and create codecs with complex field encodings.
foldcoder : Codec Session
foldcoder =
let setQueue q s = { s | queue = q } in
let setPlaying p s = { s | playing = p } in
let setLikeCats l s = { s | likeCategories = l } in
JsonCodec.fold
[ JsonCodec.foldField "queue"
(setQueue, JD.list JD.string)
(.queue >> List.map JE.string >> JE.list)
, JsonCodec.foldField "playing"
(setPlaying, JsonCodec.decoder playingSerializer)
(.playing >> JsonCodec.encoder playingSerializer)
, JsonCodec.foldField "like"
(setLikeCats, JD.dict JD.int)
(.likeCategories
>> Dict.map (always JE.int) >> Dict.toList >> JE.object
)
]
{ queue = [], playing = Nothing, likeCategories = Dict.empty }Construct a field value for use with fold.
name specifies the name of a field that will be matched and produced.
(update, decoder) specifies an update function and a decoder.
The update function is given the new value and the current state of the object
and returns a mutated object.
extract specifies a function that encodes this field's json value.
The type of codecs constructed by the library.
You can extract a Json.Decode.Decoder with
decoderand a function that constructsJson.Encoder.Valuewithencoder.