Skip to content
This repository was archived by the owner on Dec 21, 2018. It is now read-only.

Commit 801727b

Browse files
Merge pull request #12 from TechforgoodCAST/json-workshop
Json workshop
2 parents 1c12559 + 70dc36c commit 801727b

File tree

10 files changed

+443
-0
lines changed

10 files changed

+443
-0
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ Elm week at Founders & Coders
1313
+ Html docs - http://package.elm-lang.org/packages/elm-lang/html/latest
1414

1515

16+
## Tools
17+
18+
+ Online Elm Repl - http://elmrepl.cuberoot.in/
19+
+ Online Elm Program Viewer - https://ellie-app.com/new
20+
21+
1622
## Useful Resources
1723

1824
+ Cheat sheet - https://www.elm-tutorial.org/en/
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module DeeplyNested exposing (..)
2+
3+
import Html exposing (..)
4+
import SampleJson exposing (deeplyNestedString)
5+
6+
7+
main : Html msg
8+
main =
9+
text "render your solution here"
10+
11+
12+
type alias RescuedData =
13+
{ saved1 : String
14+
, saved2 : String
15+
, savedList : List Int
16+
}
17+
18+
19+
20+
{--
21+
to run your decoder call `decodeString` (from Json.Decode) with your `decoder` and a `string` of raw JSON
22+
23+
main might look something like:
24+
25+
main = text <| toString <| decodeString rescueDecoder todoString
26+
27+
You don't normally need to do the decodeString step however (Http handles this bit for you)
28+
--}

part3/json-workshop/Guardian.elm

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module Guardian exposing (..)
2+
3+
import Html exposing (..)
4+
import SampleJson exposing (guardianApiString)
5+
6+
7+
type alias Article =
8+
{ title : String
9+
, section : String
10+
, url : String
11+
}
12+
13+
14+
main : Html msg
15+
main =
16+
text "render your solution here"
17+
18+
19+
20+
{--
21+
to run your decoder call `decodeString` (from Json.Decode) with your `decoder` and a `string` of raw JSON
22+
23+
main might look something like:
24+
25+
main = text <| toString <| decodeString articlesDecoder todoString
26+
27+
You don't normally need to do the decodeString step however (Http handles this bit for you)
28+
--}

part3/json-workshop/README.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# JSON Workshop
2+
3+
This workshop will help you get to grips with writing JSON decoders rather than the full HTTP request response cycle
4+
5+
JSON in elm may seem like a weird an unnecessarily complex task at first but it can have some really positive effects on the reliability of your app!
6+
7+
8+
## Decoders
9+
10+
+ A string of JSON must be decoded (turned into `Elm` values) before it can be used in your elm app
11+
+ We write special functions called `Decoders` to do this
12+
+ These are a bit like a template of what Elm expects the JSON structure to be like
13+
14+
## Why?
15+
16+
+ JSON has a habit of exploding (try calling `JSON.parse` on an invalid JSON string in JavaScript)
17+
+ Having a schema for your JSON means you always get back the values you expect (so the rest of your app can use them)
18+
+ Errors are gracefully handled using the `Result` type (the result of parsing a string can be either `OK YourData` or `Err reason`)
19+
20+
21+
### Reference Docs and guides:
22+
23+
Elm Lang Guide section - https://guide.elm-lang.org/interop/json.html
24+
JSON Decode docs - http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Json-Decode
25+
Elm Decode Pipeline docs - http://package.elm-lang.org/packages/NoRedInk/elm-decode-pipeline/latest
26+
27+
## Exercises
28+
29+
There are 3 strings of raw JSON for you to decode into elm values (in `SampleJson.elm`), and three files set up for you (`Todo.elm`, `DeeplyNested.elm`, `Guardian.elm`)
30+
For now all you need to do is render the result of the decoder to the screen
31+
32+
### 1. Decode Todos JSON
33+
34+
Turn the string of todos JSON into a list of todos `List Todo`
35+
36+
```json
37+
{
38+
"data": {
39+
"todos": [
40+
{ "task": "Clean room", "urgent": false, "completed": false },
41+
{ "task": "Get food", "urgent": true, "completed": false },
42+
{ "task": "Learn JavaScript", "urgent": false, "completed": true },
43+
{ "task": "Finish CV", "urgent": false, "completed": false }
44+
]
45+
}
46+
}
47+
```
48+
49+
50+
### 2. Decode Deeply Nested JSON
51+
52+
This is a slightly contrived example; put each of the nested keys into the `RescuedData` record type
53+
54+
```json
55+
{
56+
"key1": {
57+
"key2": {
58+
"key3": {
59+
"key4": "help me I'm buried away!"
60+
},
61+
"key5": "save me too!"
62+
}
63+
},
64+
"random_list_of_numbers": [ 120, 23, 45, 96, 5 ]
65+
}
66+
```
67+
68+
### 3. Decode Guardian API response
69+
70+
Extract the useful data from the sample guardian api response string into a list of articles `List Article`
71+
72+
```json
73+
{
74+
"response": {
75+
"status": "ok",
76+
"total": 13724,
77+
"startIndex": 1,
78+
"pageSize": 10,
79+
"currentPage": 1,
80+
"pages": 1373,
81+
"orderBy": "relevance",
82+
"results": [
83+
{
84+
"id": "lifeandstyle/2017/aug/08/share-your-underwhelming-pictures-of-cats",
85+
"type": "article",
86+
"sectionId": "lifeandstyle",
87+
"sectionName": "Life and style",
88+
"webPublicationDate": "2017-08-08T11:09:15Z",
89+
"webTitle": "Share your underwhelming pictures of cats",
90+
"webUrl": "https://www.theguardian.com/lifeandstyle/2017/aug/08/share-your-underwhelming-pictures-of-cats",
91+
"apiUrl": "https://content.guardianapis.com/lifeandstyle/2017/aug/08/share-your-underwhelming-pictures-of-cats",
92+
"isHosted": false
93+
},
94+
{
95+
"id": "lifeandstyle/2017/may/05/experience-my-dog-rescues-cats",
96+
"type": "article",
97+
"sectionId": "lifeandstyle",
98+
"sectionName": "Life and style",
99+
"webPublicationDate": "2017-05-05T13:00:34Z",
100+
"webTitle": "Experience: my dog rescues cats",
101+
"webUrl": "https://www.theguardian.com/lifeandstyle/2017/may/05/experience-my-dog-rescues-cats",
102+
"apiUrl": "https://content.guardianapis.com/lifeandstyle/2017/may/05/experience-my-dog-rescues-cats",
103+
"isHosted": false
104+
}
105+
]
106+
}
107+
}
108+
```
109+
110+
111+
## Tips
112+
113+
+ Think of your decoders as bits of lego that you can plug together (start small and build them into bigger pieces)
114+
+ Always start with your core data structure (e.g. your `Article` or `Todo`)
115+
+ The order of the keys that you decode is the same order that they are defined in your record type (the `Article` constructor is just a function that takes 3 strings and returns an `Article`)
116+
+ Your Elm Data doesn't have to have the same nesting as the JSON! Even though the data may be deeply nested in the JSON, your decoders will most likely flatten it into a more useful structure

part3/json-workshop/SampleJson.elm

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
module SampleJson exposing (..)
2+
3+
4+
todosString : String
5+
todosString =
6+
"""
7+
{
8+
"data": {
9+
"todos": [
10+
{ "task": "Clean room", "urgent": false, "completed": false },
11+
{ "task": "Get food", "urgent": true, "completed": false },
12+
{ "task": "Learn JavaScript", "urgent": false, "completed": true },
13+
{ "task": "Finish CV", "urgent": false, "completed": false }
14+
]
15+
}
16+
}
17+
"""
18+
19+
20+
deeplyNestedString : String
21+
deeplyNestedString =
22+
"""
23+
{
24+
"key1": {
25+
"key2": {
26+
"key3": {
27+
"key4": "help me I'm buried away!"
28+
},
29+
"key5": "save me too!"
30+
}
31+
},
32+
"list_of_numbers": [ 120, 23, 45, 96, 5 ]
33+
}
34+
"""
35+
36+
37+
guardianApiString : String
38+
guardianApiString =
39+
"""
40+
{
41+
"response": {
42+
"status": "ok",
43+
"total": 13724,
44+
"startIndex": 1,
45+
"pageSize": 10,
46+
"currentPage": 1,
47+
"pages": 1373,
48+
"orderBy": "relevance",
49+
"results": [
50+
{
51+
"id": "lifeandstyle/2017/aug/08/share-your-underwhelming-pictures-of-cats",
52+
"type": "article",
53+
"sectionId": "lifeandstyle",
54+
"sectionName": "Life and style",
55+
"webPublicationDate": "2017-08-08T11:09:15Z",
56+
"webTitle": "Share your underwhelming pictures of cats",
57+
"webUrl": "https://www.theguardian.com/lifeandstyle/2017/aug/08/share-your-underwhelming-pictures-of-cats",
58+
"apiUrl": "https://content.guardianapis.com/lifeandstyle/2017/aug/08/share-your-underwhelming-pictures-of-cats",
59+
"isHosted": false
60+
},
61+
{
62+
"id": "lifeandstyle/2017/may/05/experience-my-dog-rescues-cats",
63+
"type": "article",
64+
"sectionId": "lifeandstyle",
65+
"sectionName": "Life and style",
66+
"webPublicationDate": "2017-05-05T13:00:34Z",
67+
"webTitle": "Experience: my dog rescues cats",
68+
"webUrl": "https://www.theguardian.com/lifeandstyle/2017/may/05/experience-my-dog-rescues-cats",
69+
"apiUrl": "https://content.guardianapis.com/lifeandstyle/2017/may/05/experience-my-dog-rescues-cats",
70+
"isHosted": false
71+
},
72+
{
73+
"id": "environment/commentisfree/2017/jul/11/cats-dogs-caterpillars-pets-butterflies",
74+
"type": "article",
75+
"sectionId": "environment",
76+
"sectionName": "Environment",
77+
"webPublicationDate": "2017-07-11T07:00:03Z",
78+
"webTitle": "Forget cats and dogs – caterpillars make the best pets | Patrick Barkham",
79+
"webUrl": "https://www.theguardian.com/environment/commentisfree/2017/jul/11/cats-dogs-caterpillars-pets-butterflies",
80+
"apiUrl": "https://content.guardianapis.com/environment/commentisfree/2017/jul/11/cats-dogs-caterpillars-pets-butterflies",
81+
"isHosted": false
82+
},
83+
{
84+
"id": "film/2017/jun/29/kedi-review-street-cats-of-istanbul-documentary-ceyda-torun",
85+
"type": "article",
86+
"sectionId": "film",
87+
"sectionName": "Film",
88+
"webPublicationDate": "2017-06-29T11:00:21Z",
89+
"webTitle": "Kedi review – slinking around with the street cats of Istanbul",
90+
"webUrl": "https://www.theguardian.com/film/2017/jun/29/kedi-review-street-cats-of-istanbul-documentary-ceyda-torun",
91+
"apiUrl": "https://content.guardianapis.com/film/2017/jun/29/kedi-review-street-cats-of-istanbul-documentary-ceyda-torun",
92+
"isHosted": false
93+
},
94+
{
95+
"id": "artanddesign/2017/sep/25/turner-prize-2017-exhibition-review-a-snake-infested-garden-and-fat-cats-on-horseback",
96+
"type": "article",
97+
"sectionId": "artanddesign",
98+
"sectionName": "Art and design",
99+
"webPublicationDate": "2017-09-25T17:22:56Z",
100+
"webTitle": "Turner prize 2017 exhibition review: a snake-infested garden and fat cats on horseback",
101+
"webUrl": "https://www.theguardian.com/artanddesign/2017/sep/25/turner-prize-2017-exhibition-review-a-snake-infested-garden-and-fat-cats-on-horseback",
102+
"apiUrl": "https://content.guardianapis.com/artanddesign/2017/sep/25/turner-prize-2017-exhibition-review-a-snake-infested-garden-and-fat-cats-on-horseback",
103+
"isHosted": false
104+
},
105+
{
106+
"id": "music/2017/may/29/english-national-opera-the-day-after-review-jonathan-dove",
107+
"type": "article",
108+
"sectionId": "music",
109+
"sectionName": "Music",
110+
"webPublicationDate": "2017-05-29T13:09:38Z",
111+
"webTitle": "The Day After review – devastation, dead cats and godlike grandeur",
112+
"webUrl": "https://www.theguardian.com/music/2017/may/29/english-national-opera-the-day-after-review-jonathan-dove",
113+
"apiUrl": "https://content.guardianapis.com/music/2017/may/29/english-national-opera-the-day-after-review-jonathan-dove",
114+
"isHosted": false
115+
}
116+
]
117+
}
118+
}
119+
"""
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module Solutions.DeeplyNested exposing (..)
2+
3+
import Html exposing (..)
4+
import Json.Decode exposing (Decoder, string, list, int, decodeString)
5+
import Json.Decode.Pipeline exposing (decode, required, requiredAt)
6+
import SampleJson
7+
8+
9+
main : Html msg
10+
main =
11+
text <| toString <| decodeString rescueDecoder SampleJson.deeplyNestedString
12+
13+
14+
type alias RescuedData =
15+
{ saved1 : String
16+
, saved2 : String
17+
, savedList : List Int
18+
}
19+
20+
21+
rescueDecoder : Decoder RescuedData
22+
rescueDecoder =
23+
decode RescuedData
24+
|> requiredAt [ "key1", "key2", "key3", "key4" ] string
25+
|> requiredAt [ "key1", "key2", "key5" ] string
26+
|> required "list_of_numbers" (list int)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module Solutions.Guardian exposing (..)
2+
3+
import Html exposing (..)
4+
import Json.Decode exposing (..)
5+
import Json.Decode.Pipeline exposing (..)
6+
import SampleJson
7+
8+
9+
type alias Article =
10+
{ title : String
11+
, section : String
12+
, url : String
13+
}
14+
15+
16+
main : Html msg
17+
main =
18+
text <| toString <| decodeString guardianDecoder SampleJson.guardianApiString
19+
20+
21+
guardianDecoder : Decoder (List Article)
22+
guardianDecoder =
23+
at [ "response", "results" ] (list articleDecoder)
24+
25+
26+
articleDecoder : Decoder Article
27+
articleDecoder =
28+
decode Article
29+
|> required "webTitle" string
30+
|> required "sectionName" string
31+
|> required "webUrl" string

0 commit comments

Comments
 (0)