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

Commit 70dc36c

Browse files
add json workshop
1 parent 64cbe23 commit 70dc36c

File tree

11 files changed

+361
-73
lines changed

11 files changed

+361
-73
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: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
module DeeplyNested exposing (..)
22

33
import Html exposing (..)
4-
import Json.Decode exposing (..)
5-
import Json.Decode.Pipeline exposing (..)
6-
import SampleJson exposing (..)
4+
import SampleJson exposing (deeplyNestedString)
75

86

97
main : Html msg
108
main =
11-
text <| toString <| decodeString rescueDecoder deeplyNestedString
9+
text "render your solution here"
1210

1311

1412
type alias RescuedData =
@@ -18,9 +16,13 @@ type alias RescuedData =
1816
}
1917

2018

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)
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: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,101 @@ JSON in elm may seem like a weird an unnecessarily complex task at first but it
1616
+ JSON has a habit of exploding (try calling `JSON.parse` on an invalid JSON string in JavaScript)
1717
+ Having a schema for your JSON means you always get back the values you expect (so the rest of your app can use them)
1818
+ 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: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,96 @@ deeplyNestedString =
2424
"key1": {
2525
"key2": {
2626
"key3": {
27-
"key4": "help me I'm burried away!"
27+
"key4": "help me I'm buried away!"
2828
},
2929
"key5": "save me too!"
3030
}
3131
},
32-
"list_of_numbers": [ 1, 2, 3, 4, 5 ]
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+
}
33118
}
34119
"""
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
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
module Solutions.Todo exposing (..)
2+
3+
import Html exposing (..)
4+
import Json.Decode as Decode exposing (Decoder, decodeString, string, bool, list, at)
5+
import Json.Decode.Pipeline exposing (decode, required)
6+
import SampleJson
7+
8+
9+
type alias Todo =
10+
{ task : String
11+
, completed : Bool
12+
, urgent : Bool
13+
}
14+
15+
16+
main : Html msg
17+
main =
18+
text <| toString <| decodeString todosDecoder SampleJson.todosString
19+
20+
21+
todosDecoder : Decoder (List Todo)
22+
todosDecoder =
23+
at [ "data", "todos" ] (list todoDecoder)
24+
25+
26+
todoDecoder : Decoder Todo
27+
todoDecoder =
28+
decode Todo
29+
|> required "task" string
30+
|> required "completed" bool
31+
|> required "urgent" bool
32+
33+
34+
35+
{--
36+
the decoder below is equivalent to the one above
37+
under the hood decode pipeline uses the core functions from Json.Decode
38+
the pipeline style makes it easier to add new fields -- you have to keep changing to map3, map4 etc to add more fields
39+
--}
40+
-- todoDecoder : Decoder Todo
41+
-- todoDecoder =
42+
-- Json.Decode.map3 Todo
43+
-- (field "task" string)
44+
-- (field "completed" bool)
45+
-- (field "urgent" bool)

0 commit comments

Comments
 (0)