On Monday, March 13, 2017 at 11:24:05 PM UTC, [email protected] wrote:
>
> Decode.lazy was what I was looking for, thank you!
>
> However, now I have a new problem. Here is the new definitions of the
> decoders:
>
>
> type alias Elem =
> { tag : String, attrs : List ( String, String ), body : List Template }
>
>
> type Template
> = Body String
> | Tag Elem
>
>
>
> decodeTemplate : Json.Decoder Template
> decodeTemplate =
> Json.oneOf [ Json.map Tag decodeElem, Json.map Body Json.string ]
>
>
> decodeElem : Json.Decoder Elem
> decodeElem =
> Json.map3 Elem
> (Json.field "tag" Json.string)
> (Json.field "attrs" (Json.keyValuePairs Json.string))
> (Json.field "body" (Json.list (Json.lazy (\_ -> decodeTemplate))))
>
> I got the decoder to compile, but only by using oneOf. Is there a better
> way to decode ADT's with multiple branches? Am I guaranteed to have the
> decoders be tried in the order that they are put in the list?
>
I am actually going to change this and use a Dict instead. The reason being
that I want to be able to dynamically handle any kind of 'ContentModel',
not just the ones I may currently have defined. But here is another snippet
from my code that may help you.
In my Java code, I have a class hierarchy. ContentModel can be one of three
things. So I represented this as a tagged union type in Elm. I also added a
field to the json to say which sub-class something is, and I put this in
the '@type' field on the json object. Encoders/decoders are:
type ContentModel
= TitledAsContentModel Titled
| MdContentAsContentModel MdContent
| PanelAsContentModel Panel
contentModelEncoder : ContentModel -> Encode.Value
contentModelEncoder model =
case model of
TitledAsContentModel titled ->
titledEncoder titled
MdContentAsContentModel mdContent ->
mdContentEncoder mdContent
PanelAsContentModel panel ->
panelEncoder panel
contentModelDecoder : Decoder ContentModel
contentModelDecoder =
let
toContentModel typeName =
case typeName of
"Titled" ->
map TitledAsContentModel titledDecoder
"MdContent" ->
map MdContentAsContentModel mdContentDecoder
"Panel" ->
map PanelAsContentModel panelDecoder
_ ->
Decode.fail ("unknown type: " ++ typeName)
in
field "@type" Decode.string
|> andThen toContentModel
So I decode the @type field 'andThen' apply the appropriate decoder for the
tagged union constructor that is the correct one to use.
You can use 'oneOf' and I think it does try things in the order you ask,
but it is also inefficient in that you will end up trying decoders that you
know will fail. It also may not work if you have optional fields in your
json, and they can be missing in such a way that different sub-classes
cannot really be distinguished from one another, if you see what I mean.
--
You received this message because you are subscribed to the Google Groups "Elm
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.