Search code examples
jsondecodeelm

Elm: Decoding a nested array of objects with elm-decode-pipeline


I am struggling to convert a JSON response from OpenWeatherMap using NoRedInk/elm-decode-pipeline. I have managed to decode nested objects within the JSON, but I cannot do the same for an array of objects

I've included the code I currently have. It compiles but in fails when run with FetchWeather Err BadPayload ...

JSON

{
     "weather":[{"id":804}],
     "main":{"temp":289.5},
}

CODE

type alias OpenWeatherResponse =
  { main: MainResult
  , weather: WeatherResult
  }

type alias MainResult =
  { temp: Float }

type alias ConditionResult =
  { code: Int }

decodeOpenWeatherResponse : Decoder OpenWeatherResponse
decodeOpenWeatherResponse =
    decode OpenWeatherResponse
        |> required "main" decodeMain
        |> required "weather" decodeConditions

decodeMain : Decoder MainResult
decodeMain =
    decode MainResult
        |> required "temp" float

decodeConditions : Decoder ConditionResult
decodeConditions =
     decode ConditionResult
        |> required "id" int -- This is clearly wrong --

Solution

  • You can use Json.Decode.list : Decoder a -> Decoder (List a) to make a parser of a list from a parser of an item.

    decodeConditions : Decoder (List ConditionResult)
    decodeConditions =
        Json.Decode.list decodeCondition
    
    decodeCondition : Decoder ConditionResult
    decodeCondition =
        decode ConditionResult
            |> required "id" int
    

    Or, you can make it look like a part of the pipeline:

    decodeConditions : Decoder (List ConditionResult)
    decodeConditions=
        decode ConditionResult
            |> required "id" int
            |> Json.Decode.list
    

    Also, the response type has to have a list:

    type alias OpenWeatherResponse =
      { main : MainResult
      , weather : List ConditionResult
      }
    

    If you just want the first item of the weather array, you can use Json.Decode.index : Int -> Decoder a -> Decoder a:

    decodeConditions : Decoder ConditionResult
    decodeConditions =
         decode ConditionResult
            |> required "id" int
            |> Json.Decode.index 0