Reputation: 53
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.
Upvotes: 4
Views: 561
Reputation: 53
Here is my working decoder which can now parse polygons:
module Main exposing (main)
import Html exposing (Html, text)
import Json.Decode exposing (decodeString, string, list, at)
import Json.Decode.Pipeline exposing (decode, required, optional)
jsonString =
"{\"shapes\":[\n{\n\"shape\": \"circle\",\n \"x\": \"50\",\n \"y\": \"50\",\n \"r\": \"40\"\n},\n{\n\"shape\": \"rect\",\n \"x\": \"100\",\n \"y\": \"100\",\n \"width\": \"50\",\n \"height\": \"60\"\n},\n{\n\"shape\": \"polygon\",\n\"points\":[\n{\n\"x\": \"80\",\n\"y\": \"80\"\n}\n]}\n]}"
main : Html msg
main =
text <| toString <| decodeString decoder jsonString
type Shape
= Circle String String String
| Rectangle String String String String
| Polygon (List ( ( String, String ) ))
decoder =
at ["shapes"] (list shapeDecoder)
shapeDecoder =
decode toShape
|> required "shape" string
|> optional "x" string ""
|> optional "y" string ""
|> optional "r" string ""
|> optional "width" string ""
|> optional "height" string ""
|> optional "points" (list pointDecoder) []
pointDecoder =
decode toPoint
|> required "x" string
|> required "y" string
toShape shape x y r width height points =
case shape of
"circle" ->
Circle x y r
"rect" ->
Rectangle x y width height
"polygon" ->
Polygon points
_ ->
Circle "" "" ""
toPoint x y =
(x, y)
The Json looks like this:
{ "shapes":[
{
"shape": "circle",
"x": "50",
"y": "50",
"r": "40"
},
{
"shape": "circle",
"x": "300",
"y": "50",
"r": "40"
},
{
"shape": "rect",
"x": "100",
"y": "100",
"width": "50",
"height": "60"
},
{
"shape": "polygon",
"points":
[ { "x":"200", "y":"150"}
, { "x":"250", "y":"400"}
, { "x":"120", "y":"340"}
, { "x":"160", "y":"250"}
, { "x":"180", "y":"170"}
]
}
]
}
You can try it here: Elli-App
Upvotes: 1
Reputation: 2480
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
Upvotes: 5