3

In Elm 0.18, I would like to build a JSON decoder for the following examples:

case 1:

{"metadata": {"signatures":[{"metadata": {"code": "1234"}},
                            {"metadata": {"code": "5678"}}]}}  

-> { code = Just "1234" }

case 2:

{"metadata": {"signatures":[]}} 

-> { code = Nothing }

case 3:

{"metadata": {"signatures":[{"metadata": null}]}} 

-> { code = Nothing }

This is what I got working, but it fails for case 3.

type alias Code = { code : Maybe String }

let
    js = """{"metadata": {"signatures":[{"metadata": {"code": "1234"}},
                   {"metadata": {"code": "5678"}}]}}"""

    dec1 =
        Decode.at [ "metadata", "code" ] Decode.string

    dec0 =
        Decode.list dec1
            |> Decode.andThen
                (\v ->
                    if List.isEmpty v then
                        Decode.succeed Nothing
                    else
                        Decode.succeed <| List.head v
                )

    dec =
        decode Code
            |> optionalAt [ "metadata", "signatures" ] dec0 Nothing

    expected =
        Ok { code = Just "1234" }
in
    Decode.decodeString dec js
        |> Expect.equal expected

A workaround would be to import all the data to the model and then obtain the info from the model, but I prefer to avoid adding unnecessary data into my model. How can I improve this?

Francisco Dibar
  • 353
  • 1
  • 5
  • What output are you getting for the second string? I ran your code and I'm getting `Ok { code = Nothing }` for the second string. – Dogbert Jun 28 '18 at 13:48
  • 1
    Your decoder [works for me](https://ellie-app.com/D7RFyXhzxLa1). Note that `dec0` can be shortened to `Decode.list dec1 |> Decode.map List.head` – Chad Gilbert Jun 28 '18 at 13:59
  • Working example of your code is at https://ellie-app.com/D8WTFxc73Sa1 – bdukes Jun 28 '18 at 15:09
  • You guys are right, it works for both cases, I added the third one which is the problematic one. – Francisco Dibar Jun 28 '18 at 19:04

1 Answers1

1

A more simplified approach could use Json.Decode.index to force the decoding at index zero as a string if it exists, which will fail otherwise, so you can use Json.Decode.maybe to return Nothing on failure.

dec0 =
    Decode.maybe (Decode.index 0 dec1)
Chad Gilbert
  • 36,115
  • 4
  • 89
  • 97