Reputation: 135
I'm building an application with postgrest (backend) and elm (frontend) and right now I'm stuck writing a decoder. I don't quite understand how to decode to my specific type instead of the base type decoders like int and string.
When I take a look at how string
is implemented (line 73 to 75) it's just a call to Elm.Kernel.Json.decodeString
which in turn is in the js base of elm.
My JSON looks something like this:
{ "id" : 1
, "parent_id" : 1
, "body" : "text of my body"
}
OR
{ "id" : 1
, "parent_id" : 1
, "sub_parent_id" : 2
}
Is it possible to decode something like that into a single record type (Step
) which contains a custom type with multiple constructors to match the two different fields (sub_parent_id
and body
)
My decoder looks like this but doesn't compile:
import Api.Parent.Step.Types exposing ( Step, StepContent )
import Json.Decode exposing (..)
import Json.Decode.Pipeline exposing (..)
decoder : Decoder Step
decoder =
succeed Step
|> required "id" int
|> oneOf
[ field "stepContent" stepBodyDecoder
, field "stepContent" subStepDecoder
]
stepBodyDecoder : Decoder StepContent
stepBodyDecoder =
succeed StepContent
|> required "body" string
subStepDecoder : Decoder StepContent
subStepDecoder =
succeed StepContent
|> required "sub_parent_id" decoder
Any my Types:
module Api.Parent.Step.Types exposing ( Step, StepContent )
type StepContent
= StepBody String
| SubStep Step
type alias Step =
{ id : Int
, stepContent : StepContent
}
Upvotes: 1
Views: 541
Reputation: 1008
The solution is written in the types (as often)
oneOf : List (Decoder a) -> Decoder a
So we have to supply a list of Decoder StepContent
, which are the decoder functions.
And as per the relevant comment, to build a StepContent
value, we need to form either constructor : a StepBody
or a SubStep
.
import Api.Parent.Step.Types exposing ( Step, StepContent )
import Json.Decode exposing (..)
import Json.Decode.Pipeline exposing (..)
decoder : Decoder Step
decoder =
succeed Step
|> required "id" int
|> required "stepContent" (oneOf [ stepBodyDecoder, subStepDecoder ] )
stepBodyDecoder : Decoder StepContent
stepBodyDecoder =
succeed StepBody
|> required "body" string
subStepDecoder : Decoder StepContent
subStepDecoder =
succeed SubStep
|> required "sub_parent_id" decoder
Upvotes: 0
Reputation: 29146
JSON decode pipelines expects succeed
to be passed a function, and StepContent
is not a function, but a type. Variant constructors are functions however, and if you look at the compiler error it suggests the right fix (although that's somewhat coincidental since it just suggest based on similar names):
I cannot find a `StepContent` variant:
28| succeed StepContent
^^^^^^^^^^^
These names seem close though:
StepBody
Step
OneOf
SubStep
StepBody
and SubStep
are the ones you should use instead. stepBodyDecoder
will work with just that change, and will get you at least one step further with subStepDecoder
, but the type and decoder otherwise don't match the JSON. sub_parent_id
is a number, not an object, so it seems like SubStep
should take an Int
instead of a Step
. You can then possibly construct a separate hierarchical data structure in a subsequent step.
Upvotes: 1