Set default values while decoding JSON

I want to decode JSON, but my type alias needs more information than the JSON provides. I use this type alias:

type alias Location =
  { id : Int
  , name : String
  , checked : Bool
  }

The JSON only contains the fields id and name. The field checked should be True by default. I have no idea where to add this boolean.
So far I have this:

decodeLocations : Decoder (List Location)
decodeLocations = 
  Dec.field "locations" ( Dec.list ( Dec.map2 Location
      ( Dec.field "id" Dec.int )
      ( Dec.field "name" Dec.string )
    ) )

...

type Msg 
  = GotLocations ( Result Http.Error ( List Location ) )

This results in the compiler stating a type mismatch:

This `field` call produces:

    Decoder (List (Bool -> Location))

But the type annotation on `decodeLocations` says it should be:

    Decoder (List Location)

I understand this mismatch, but where could I add the Bool?

Thanks in advance!

D.field "locations" <| D.list <|
  D.map3 Location
    ( D.field "id" D.int )
    ( D.field "name" D.string )
    ( D.succeed True )

You use the .succeed decoder. It does not evaluate anything, but gives a sucessful decoding of whatever value you pass to it :slight_smile:

2 Likes

It’s also possible to stick with map2 by replacing Location with a lambda function:

decodeLocations : Decoder (List Location)
decodeLocations =
    Dec.field "locations"
        (Dec.list
            (Dec.map2 (\id name -> { id = id, name = name, checked = True })
                (Dec.field "id" Dec.int)
                (Dec.field "name" Dec.string)
            )
        )
2 Likes

Thank you both. Both approaches help me to discover possibilities I would never think of.
Thanks Atlewee to use the <| operator as well, showing me how to use it in this situation.
I think I will go with the Dec.succeed, because it keeps the name of the type in the function.

Any suggestions on a good beginners tutorial on Json decoding? (I read “An Introduction to Elm” of course.)

I think I will go with the Dec.succeed, because it keeps the name of the type in the function

Just for full understanding, you can keep “the name of the type” in the function in the map2 situation too:

decodeLocations : Decoder (List Location)
decodeLocations =
    Dec.field "locations"
        (Dec.list
            (Dec.map2 (\id name -> Location id name True)
                (Dec.field "id" Dec.int)
                (Dec.field "name" Dec.string)
            )
        )

The map3 version can also be written that way:

decodeLocations : Decoder (List Location)
decodeLocations =
    Dec.field "locations"
        (Dec.list
            (Dec.map3 (\id name checked -> Location id name checked)
                (Dec.field "id" Dec.int)
                (Dec.field "name" Dec.string)
                (Dec.succeed True)
            )
        )

However, directly calling Location like that is usually frowned upon. It’s much more clear to just “build the record instead”: { id = id, name = name, checked = checked }. But it’s good to know that when you make a type alias Foo for a record (yes, only for records!), you get not only a type named Foo, Elm also auto-generates a function called Foo that takes all the stuff in the record in order and builds such a record. It is that auto-generated function that you use in the body of decodeLocations, while the type with the same name is used in the decodeLocations : Decoder (List Location) type annotation.

Yet one way of doing it is exploiting the fact that the auto-generated function takes the record contents in a specific order. So you could move checked : Bool first in the record, then you could do (but I’m not saying it’s a good solution!):

type alias Location =
  { checked : Bool
  , name : String
  , id : Int
  }

decodeLocations : Decoder (List Location)
decodeLocations =
    Dec.field "locations"
        (Dec.list
            (Dec.map2 (Location True)
                (Dec.field "id" Dec.int)
                (Dec.field "name" Dec.string)
            )
        )

You don’t need to understand all of those details right now, but it might be handy some time! Either way, I like @Atlewee’s solution best too.

Any suggestions on a good beginners tutorial on Json decoding?

This blog post was enlightening for me at least:

Thanks a lot, lydell, for elaborating on the subject. It is helping me a lot!

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.