3

had a look for something like this, but can't find the exact issue.

I have a JSON back from server side validation that looks like:

{ 
  "field": ["field-name"], 
  "messages":["message","message"]
}

What I would like to do is decode it into an elm record like

{ field: String, messages: List String }

However, I'm having trouble with the err, field field. I'm having trouble turning a single element JSON array into just a string of that element.

Is it even possible with Decode, or am I better of Decoding it into a List and then just grabbing the head from the list.

This is what I have for the decode:

valErrorDecoder : Decode.Decoder ValError
valErrorDecoder =
decode ValError
    |> required "field" (Decode.list Decode.string)
    |> required "messages" (Decode.list Decode.string)

Thanks for any help!

Chad Gilbert
  • 36,115
  • 4
  • 89
  • 97
m4v15
  • 73
  • 7
  • I'v met the same issue. Tried both solutions. However, if empty list occurred in Json, "Maybe" is needed. Decode.index will not catch the fail. – su hui Jun 28 '19 at 06:46

2 Answers2

11

Try Decode.index, that should do the trick.

valErrorDecoder : Decode.Decoder ValError
valErrorDecoder =
decode ValError
    |> required "field" (Decode.index 0 Decode.string)
    |> required "messages" (Decode.list Decode.string)
bdukes
  • 152,002
  • 23
  • 148
  • 175
JosephStevens
  • 1,668
  • 1
  • 15
  • 17
4

You mention in a comment that a colleague suggested Decode.map. In case you're curious, here is what that (more complex) solution might look like:

firstElementDecoder : Decode.Decoder a -> Decode.Decoder a
firstElementDecoder baseDecoder = Decode.list baseDecoder
 |> Decode.map List.head
 |> Decode.andThen (Maybe.map Decode.succeed >> Maybe.withDefault (Decode.fail "Empty list"))

What's happening here? We begin by decoding a list of strings, then map the List.head function onto that list, giving a Decoder (Maybe String). The function

Maybe.map Decode.succeed 
  >> Maybe.withDefault (Decode.fail "Empty list")

takes in a Maybe and turns it into a decoder, which either succeeds (with the value of the maybe) or fails (with an "Empty list" error message). We use this function as the argument to Decode.andThen, which:

  1. Passes the Maybe from the list decoder into the function above, getting either a Decode.succeed or a Decode.fail
  2. Runs the returned decoder, succeeding or failing with the appropriate value.

So, yes, Decode.index 0 is easier! But it may be of interest to see the longer solution, too :-)

Alex Lew
  • 2,064
  • 14
  • 17
  • Thanks for adding this extra answer. It enabled me finally to build a Decoder Char that only succeeds if the input is a single character string. It also shows how arbitrary extra validation can be inserted into the Decoder chain. – Mark Farmiloe May 12 '18 at 21:13