Search code examples
jsonelm

Conditional JSON decoding based on a field value


I have a need to decode JSON into an elm type like below:

Type

type User = Anonymous | LoggedIn String

type alias Model =
  { email_id : User
  , id : Id
  , status : Int
  , message : String
  , accessToken : AccessToken
  }

JSON Message 1

{
  "status": 0,
  "message": "Error message explaining what happened in server"
}

into type value

Model {
   "email_id": Anonymous
   , id: 0
   , status: 0
   , message: json.message
   , accessToken: ""
}

JSON Message 2

{
  "status": 1,
  "email_id": "asdfa@asdfa.com"
  "token": "asdfaz.adfasggwegwegwe.g4514514ferf"
  "id": 234
}

into type value

Model {
   "email_id": LoggedIn json.email_id
   , id: json.id
   , status: json.status
   , message: ""
   , accessToken: json.token
}

Decoder information

Above, "message" is not always present and email_id/id/token are always not present.

How to do this type of conditional decoding in elm


Solution

  • Json.Decode.andThen lets you do conditional parsing based on the value of a field. In this case, it looks like you'll first want to pull out the value of the "status" field, andThen handle it separately based on whether it is a 1 or 0.

    Edit 2016-12-15: Updated to elm-0.18

    import Html as H
    import Json.Decode exposing (..)
    
    type User = Anonymous | LoggedIn String
    
    type alias Id = Int
    
    type alias AccessToken = String
    
    type alias Model =
      { email_id : User
      , id : Id
      , status : Int
      , message : String
      , accessToken : AccessToken
      }
    
    modelDecoder : Decoder Model
    modelDecoder =
      (field "status" int) |> andThen modelDecoderByStatus
    
    modelDecoderByStatus : Int -> Decoder Model
    modelDecoderByStatus status =
      case status of
        0 ->
          map5
            Model
            (succeed Anonymous)
            (succeed 0)
            (succeed status)
            (field "message" string)
            (succeed "")
        1 ->
          map5
            Model
            (map LoggedIn (field "email_id" string))
            (field "id" int)
            (succeed status)
            (succeed "")
            (field "token" string)
        _ ->
          fail <| "Unknown status: " ++ (toString status)
    
    main = H.div []
      [ H.div [] [ decodeString modelDecoder msg1 |> Result.toMaybe |> Maybe.withDefault emptyModel |> toString |> H.text ]
      , H.div [] [ decodeString modelDecoder msg2 |> Result.toMaybe |> Maybe.withDefault emptyModel |> toString |> H.text ]
      ]
    
    emptyModel = Model Anonymous 0 0 "" ""
    
    msg1 = """
    {
      "status": 0,
      "message": "Error message explaining what happened in server"
    }
    """
    
    msg2 = """
    {
      "status": 1,
      "email_id": "asdfa@asdfa.com"
      "token": "asdfaz.adfasggwegwegwe.g4514514ferf"
      "id": 234
    }
    """