This is day 17 of my 30 day Elm challenge
About today's project
Code/demo: https://ellie-app.com/bYbhrKjmhJCa1
In today's project, I was finally able to decode some (hardcoded) JSON!
Bukkfrig's comment on yesterday's post saved the day, so thanks!
I'm happy with today's progress, and have other plans for the evening, so I'll leave the Python backend integration for later.
Some of the code here could be more elegant, but I'll save that for another day.
1. Describing the data
An individual planet in my JSON looks like this:
"Jupiter":{
"lightMinutes":49.561547588282494,
"xyz":[-15.160997437594864,35.36776769042324,86.01557267383876]
}
As you can see, the data and descriptions match:
type alias Planet =
{ lightMinutes : Float, xyz : List Float }
planetDecoder : String -> Decoder Planet
planetDecoder planetName =
field planetName
(map2 Planet
(field "lightMinutes" float)
(field "xyz" (list float))
)
2. Presenting the data (or the error)
planetDiv : String -> Planet -> Html msg
planetDiv p { lightMinutes, xyz } =
div []
[ h1 [] [ text p ]
, text ((lightMinutes |> round |> String.fromInt) ++ " light minutes away")
, br [] []
, div [] <| (xyz |> List.map (\n -> text ((n |> String.fromFloat) ++ ", ")))
]
showPlanetOrError : String -> Html msg
showPlanetOrError planetName =
case
decodeString (planetDecoder planetName) hardcodedData
of
Ok planet ->
planetDiv planetName planet
Err err ->
pre []
[ text "Oops: "
, br [] []
, text (" " ++ Debug.toString err)
]
planetDiv
is a pretty basic function that just returns an Html msg
.
showPlanetOrError
either shows the planetDiv
, or logs out the error message.
3. Showing all planets
This kind of hardcoded solution takes me back to when I first learned JavaScript. Oh man. :D
main : Html msg
main =
div [] <|
List.map (\planet -> showPlanetOrError planet)
[ "Sun"
, "Mercury"
, "Venus"
, "Mars"
, "Jupiter"
, "Saturn"
, "Uranus"
, "Neptune"
, "Pluto"
]
4. Summary
JSON in Elm is tricky, and I'll probably make a few more mistakes, but the community is really helpful.
Thanks to everyone for helping a stranger in need! :)
Top comments (4)
You still have this wierd function
showPlanetOrError
which doesJson.Decode
stuff andHtml
stuff.The problem comes from the shape of your JSON. You are using the name of a field to represent what is actually data. You end up having to instruct the machine on how to handle a "Venus" field and a "Mars" field, etc. It might be human-readable, but it's not very machine readable.
Is it within your power to reshape the JSON sent by your API, so it's just a list of planets, and the planets have a
name
field?In this Ellie (ellie-app.com/bYpVTc2KhSta1), there are two different versions of the same thing:
Yeah, the original JSON is structured poorly. At first I did want to change it the way you describe, but I decided to keep it.
My thinking was that I might have to deal with other poorly structured JSON APIs in the future, but as you say, it has only made things more difficult.
Your second example is really nice to read - it's this kind of code I want to write. I would have gotten there sooner if I hadn't been so stubborn about the JSON shape, so that's a good lesson.
Also, I gotta say your first example is spectacular - the duplication at line 29 and 30 (List.map2 viewUnnamedPlanet) the nesting at line 78 (bodiesDecoder) are pure poetry. :D
Good work!
JSON decoding/encoding is one of the trickier parts of Elm. I rarely make it all the way without looking at the docs. But it becomes easier every time you do it.
It's one of those things that gives your app a rock solid foundation and prevents stupid runtime exceptions. It's part of what could make you considerably happier than the average JS/TS developer in the long run 😄
Happy to see that you're getting into JSON decoding a little more! I also struggled with it initially, but really love it now. I think for me, understanding JSON decoding is what got me to finally understand Elm and Haskell.
Can't wait for Day 18!