On Monday, March 13, 2017 at 11:24:05 PM UTC, vis...@stanford.edu 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 elm-discuss+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to