I would like to decode an API response in which one of the fields value (category
) would determine how to decode another field (configuration
) using different sub-decoders.
I was able to accomplish such thing using Json.Decode.mapn
functions and the andThen
function, but I was wondering if there is any way to do such thing using elm-decode-pipeline
as it has a nicer API and I will run out of mapn
functions eventually.
A minimmum and somewhat trivial example would be like this:
type alias Machine =
{ name : String
, specs : MachineSpecs
}
type MachineSpecs
= ElectricMachine ElectricSpecs
| MechanicalMachine MechanicalSpecs
| UnknownMachine
type alias ElectricSpecs =
{ voltage : Int
}
type alias MechanicalSpecs =
{ gears : Int
}
And some valid JSON responses would have these shapes:
{
"name": "Foo electric machine",
"category": "electric",
"configuration": {
"voltage": 12
}
}
{
"name": "Bar mechanical machine",
"category": "mechanical",
"configuration": {
"gears": 5
}
}
{
"name": "Some machine of unknown category",
"category": "foo"
}
I tried a similar approach to the one I was using with the mapn
functions, but it doesn't work.
decoder : Decoder Machine
decoder =
decode Machine
|> required "name" string
|> required "category" (string |> andThen catDec)
catDec : String -> Decoder MachineSpecs
catDec cat =
case cat of
"electric" ->
map ElectricMachine electricDecoder
"mechanical" ->
map MechanicalMachine mechanicalDecoder
_ ->
succeed UnknownMachine
electricDecoder : Decoder ElectricSpecs
electricDecoder =
decode ElectricSpecs
|> requiredAt [ "configuration", "voltage" ] int
mechanicalDecoder : Decoder MechanicalSpecs
mechanicalDecoder =
decode MechanicalSpecs
|> requiredAt [ "configuration", "gears" ] int
In fact, I haven't seen any example on the web or docs using both Json.Decode.Pipeline
and andThen
at the same time, so I'm not sure if it's even possible.
I have set up an online example of this issue showing how it fails to decode the conditional part: https://runelm.io/c/3ut
As an alternative, you could place your andThen
bindings before the pipeline (ellie example):
decoder : Decoder Machine
decoder =
field "category" string
|> andThen catDec
|> andThen
(\cat ->
decode Machine
|> required "name" string
|> hardcoded cat
)
If you are running out of mapN
numbers, consider switching to andMap
(or the infix version |:
) in the elm-community/json-extra
package.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With