job.answiz.com
1 Answer
  • 1
Votes
name
name Punditsdkoslkdosdkoskdo

Parsing JSON in Elm

I want to parse a JSON like this:

{ "shapes":[
{
    "shape": "circle",
    "x": "50",
    "y": "50",
    "r": "40"
},
{
    "shape": "rect",
    "x": "100",
    "y": "100",
    "width": "50",
    "height": "60"
},
]}

and this is my Elm code:

type alias Model =
    { input : Shape
    , saved : List Shape
    , modal1 : String
    , modal2 : String
    , modal3 : String
    , region : String
    }

type Shape
    = Circle String String String
    | Rectangle String String String String

type Msg
    = LoadJson
    | JsonLoaded (Result Http.Error (List Shape))

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of

    LoadJson -> 
        (model, getJson)

    JsonLoaded (Ok shapes) -> 
        ({ model | saved = shapes }, Cmd.none)

    JsonLoaded (Err _) ->
        (model, Cmd.none)

getJson =
    let
        url =
          "http://www.mywebsite.de/shapes.json"
    in
        Http.send JsonLoaded (Http.get url decoder)

decoder = 
    at ["shapes"] (list shapeDecoder)

shapeDecoder = 
    decode func 
        |> required "shape" string
        |> required "x" string
        |> required "y" string
        |> optional "r" string ""
        |> optional "width" string ""
        |> optional "height" string ""

func shape x y r width height = 
    case shape of
        "circle" ->
            Circle x y r

        "rect" ->
            Rectangle x y width height

        _ -> 
            Circle "" "" ""

All shapes in the list which is called "saved" are supposed to be listed in a table.

If I write some elements in this list, they are shown in the table but not if I'm trying to get them from my JSON. Something has to be wrong with my decoder but I don't know what.

Thank you for your help.

 

You can simplify your decoder to this. Using the oneOf we can try decoding the json against a decoder for each shape. I can't run this against your website because that's not a real website, but you can run a demo of this decoder here https://ellie-app.com/cFrSPbrYna1/0

Running this decoder against the json you provided, you'd get

Ok ([Circle "50" "50" "40",Rectangle "100" "100" "50" "60"])

Decoder:

import Json.Decode as Decode

decoder =
    Decode.at [ "shapes" ] (Decode.list decodeShape)


decodeShape =
    Decode.oneOf [ circleDecode, rectDecode ]


circleDecode =
    Decode.map4
        circle
        (Decode.field "shape" Decode.string)
        (Decode.field "x" Decode.string)
        (Decode.field "y" Decode.string)
        (Decode.field "r" Decode.string)


rectDecode =
    Decode.map5
        rect
        (Decode.field "shape" Decode.string)
        (Decode.field "x" Decode.string)
        (Decode.field "y" Decode.string)
        (Decode.field "width" Decode.string)
        (Decode.field "height" Decode.string)


circle shape x y r =
    -- Here you can, if you wanted to, have an assertion that the
    -- `shape` is indeed a "circle" and not something else
    -- In which case you probably want to return something like
    -- `Maybe Shape` or `MaybeCircle` instead of just a Circle.
    -- Same thing for the `rect` function below
    Circle x y r


rect shape x y w h =
    Rectangle x y w h
  • 0
Reply Report