Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to conditionally decode certain fields using elm-decode-pipeline

Tags:

elm

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

like image 376
Pablo Enrici Avatar asked Dec 23 '22 22:12

Pablo Enrici


1 Answers

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.

like image 51
Chad Gilbert Avatar answered Mar 01 '23 23:03

Chad Gilbert