When receiving JSON data from other resources(server API etc), we need [Json.Decode](http://package.elm-lang.org/packages/elm-lang/core/3.0.0/Json-Decode) to convert the JSON values into Elm values. This gist let you quickly learn how to do that. I like to follow working example code so this is how the boilerplate will look like: ```elm import Graphics.Element exposing (Element, show) import Task exposing (Task, andThen) import Json.Decode exposing (Decoder, int, string, object3, (:=)) import Http {- declare data type here -} type alias SimpleRecord = { name : String , description : String } -- initialize mailbox with the type declared above mailbox = Signal.mailbox (SimpleRecord "" "") -- VIEW main : Signal Element main = Signal.map show mailbox.signal -- TASK fetchApi = Http.get decoder api handleResponse data = Signal.send mailbox.address data -- decoder changes depends on our data type decoder = ... port run : Task Http.Error () port run = fetchApi `andThen` handleResponse api = "http://some-api-url.com" ``` So here we have a mailbox - `mailbox` which contains initial data depending on the data type. When the application starts, `fetch` is called and the response is handled by `handleResponse`. Our `main` function will display the value of `mailbox` value. Here, we will fetch data github api and display it on screen. So to follow along this tutorial, you have to modify 1. data type (currently SimpleRecord) 2. initial data in `mailbox` 3. decoder function 4. api (to get different data) Notice that I am omitting some data annotations for brevity. Our main focus in this post will be `decoder` function. We will see how to utilize the Json.Decoder library when dealing with different type of data. ## Objects ### Object -> tuple First of all let's fetch the elm-lang/core repository. The API looks like: ``` https://api.github.com/repos/elm-lang/core ``` If you paste this on browser, you will see something like: ```json { "id": 25231002, "name": "core", ... "subscribers_count": 47 } ``` Let's say we are interested in 3 fields - `name`, `description` and `watchers_count` of this repository and we want to display it in simple tuple: ie (name, description, watchers_count). Here is how our data type looks like: ```elm type alias RepoTuple = ( String, String, Int ) mailbox = Signal.mailbox ("", "", 0) ``` We also need to declare the initial data in mailbox - ("", "", 0). Because our API returns a JSON object, the Object fields in [doc](http://package.elm-lang.org/packages/elm-lang/core/3.0.0/Json-Decode#) will give us what we need. In our case we need 3 fields, so we will use `object3`. Elm supports `object1` to `object8` and we have to decide depends on how many value we need. ```elm object3 : (a -> b -> c -> value) -> Decoder a -> Decoder b -> Decoder c -> Decoder value ``` The first argument `(a -> b -> c -> value)` is a function which takes 3 arguments and return a `value`. In Elm, we know that ```elm (,,) "Name" "Description" 100 -- ("Name","Description",100) : ( String, String, number ) ``` So our decoder would be: ```elm repoTupleDecoder : Decoder RepoTuple repoTupleDecoder = object3 (,,) ("name" := string) ("description" := string) ("watchers_count" := int) ``` The `:=` operator is use to extract the field with the given name. Since we are interested in name, description and watchers_count so we declare it explicitly and state it's type. ```elm fetchApi = Http.get repoTupleDecoder api ``` And this is the [working code](https://gist.github.com/yang-wei/2d1c33b9c114fcd652ff#file-repotuple-elm) for this example. ### Object -> record Often tuple doesn't give enough information, in most case we want to preserve the value of field itself. So our type will look like: ```elm type alias RepoRecord = { name : String , description : String , watchers_count : Int } mailbox = Signal.mailbox (RepoRecord "" "" 0) ``` Note that in this case our data type is Record so we can initialize it by `RepoRecord "" "" 0` which will return `{ name = "", description = "", watchers_count = 0 }` which is cool. What cooler is we can even reuse it in our decoder: ```elm repoRecordDecoder : Decoder RepoRecord repoRecordDecoder = object3 RepoRecord ("name" := string) ("description" := string) ("watchers_count" := int) ``` Do remember to change the function name in `fetchApi`: ```elm fetchApi = Http.get repoRecordDecoder api ``` Now we will get a nice record: ```elm { name = "core", description = "Elm's core libraries", watchers_count = 338 } ``` Again, for your reference the source is [here](https://gist.github.com/yang-wei/2d1c33b9c114fcd652ff#file-repotuple-elm). ### Nested Object When we hit the [API](https://api.github.com/repos/elm-lang/core), it returns an object which contains the owner of repository in a nested object. ```json { ... "full_name": "elm-lang/core", "owner": { "login": "elm-lang", "id": 4359353, "avatar_url": "https://avatars.githubusercontent.com/u/4359353?v=3" ... }, ... } ``` Let's look at how we can retrive the value in **owner** object. We can of course create nested decoder by using 2 times of `object3` but we can avoid this by using `[at](http://package.elm-lang.org/packages/elm-lang/core/3.0.0/Json-Decode#at)` ```elm at : List String -> Decoder a -> Decoder a ``` Changing everything we need: ```elm type alias OwnerRecord = { login : String , id : Int , avatar_url : String } mailbox = Signal.mailbox (OwnerRecord "" -1 "") ``` Because the data we need have only single layer of nested field, so the first argument passed to `at` only has one value, that is `["owner"]`. ```elm ownerDecoder : Decoder OwnerRecord ownerDecoder = let decoder = object3 OwnerRecord ("login" := string) ("id" := int) ("avatar_url" := string) in at ["owner"] decoder ``` ### Any object -> List (Tuple) Let's hit another API. ``` https://api.github.com/repos/elm-lang/elm-lang.org/languages ``` and it's result is: ```json { "Elm": 400423, "JavaScript": 352902, "CSS": 75013, "Haskell": 28719, "HTML": 965 } ``` In this case, we want all values. ```elm type alias Languages = List (String, Int) mailbox = Signal.mailbox [] ``` Elm provides a very handy function - `keyValuePairs` ```elm import import Json.Decode exposing (..., keyValuePairs) ;; ... decoder : Decoder (List (String, Int)) decoder = keyValuePairs int ``` And we have all languages in tuple: ```elm [ ("HTML", 965) , ("Haskell", 28719) , ("CSS", 75013) , ("JavaScript", 352902) , ("Elm", 400423) ] ``` ### Any object -> Dict Besides `keyValuePairs`, we also has `[Decoder.dict](http://package.elm-lang.org/packages/elm-lang/core/3.0.0/Json-Decode#dict)` to turn object into dictionary. ```elm type alias Languages = Dict String Int mailbox = Signal.mailbox Dict.empty decoder : Decoder (Dict String Int) decoder = dict int ``` ## Arrays ### Array -> tuple In the previous section, we had seen multiples way to deal with object typed JSON value. However sometimes, we have an array. For example: ``` https://api.github.com/search/repositories?q=language:elm&sort=starts&language=elm ``` This API returns repositories written in Elm. ```json "total_count": 1816, "incomplete_results": false, "items": [ { "id": 4475362, "name": "elm-lang.org", "full_name": "elm-lang/elm-lang.org", ... }, { "id": 25231002, "name": "core", "full_name": "elm-lang/core", ... } ] ``` The field we are particularly interested in is the items field. Let's say we want a list which contains the `full_name` of the Elm repository. ```elm ["elm-lang/elm-lang.org", "elm-lang/core" ...] ``` Let's initialize our data first: ```elm mailbox = Signal.mailbox [] ``` First of all, here is our decoder to extract the `full_name` value ```elm fullNameDecoder : Decoder String fullNameDecoder = object1 identity ("full_name" := string) ``` Because our data is nested in the `items` field, we have to access it using the `at` operator (hope you still remember): ```elm decoder = at ["items"] _ ``` The `items` will give us an array which contains object, so we will use `[Decoder.list](http://package.elm-lang.org/packages/elm-lang/core/3.0.0/Json-Decode#list)`: ```elm list : Decoder a -> Decoder (List a) ``` `Decoder.list` takes a decoder and returns another decoder which can handle list. This suits our case: ``` decoder = at ["items"] (list fullNameDecoder) ``` Now if you wish to also extract other fields, you just have to change your `fullNameDecoder`. The source of this example is shown [here](https://gist.github.com/yang-wei/2d1c33b9c114fcd652ff#file-listdecoder-elm).