Decoding JSON strings in Elm is a 2-step process:
This lesson gives a couple basic examples of the two steps in action.
Here I am at elmlang.org/try. This is a nice little online interpreter that'll let us execute basic Elm code, as long as it doesn't use any third-party libraries from package.elmlang.org. As you can see here, I've imported the text function from HTML so that I can render strings to the screen.
Here I call text with a string, and I get something out on the other side, "Hello there." Now right above main, I'm going to define a new constant called num. I'm going to set it to a string containing a number. I'd like to figure out how to get an actual number out of this string.
We can do that using JSON decoding. Elm comes with some libraries built-in that are made to do just that. We can get access to those by importing json.decode, exposing decode string, and int. Now in order to explain these two things, I need to explain that Elm does JSON decoding in two steps.
The first step is to define a recipe, if you will, which we call a decoder. The second step is to execute the recipe, or the decoder. Int, right here, that we've just imported from JSON decode, is a recipe. It's a recipe that tells Elm how to get a number or an integer out of a string.
Decode string is a function that takes a decoder, like int, and a string, like num right here. Either it'll return an error string, if something went wrong in the decoding, or it'll return the type that the decoder was meant for, in this case an int.
Let's use these two things now to see if we can get this number out of the string. Down here below num, let's make a new constant that'll be the result of running the decoder. I'm going to call it decoded. Just for sanity's sake, I'm going to give it the type I expect so that the compiler can help us in case we write incorrect code.
I'm expecting decoded to be a result, string int. This means that what I'm expecting to get out of running decode string is something that's either a string of an error, or the value that we were looking for, an int.
Now let's fill in the definition for the declaration. Decoded equal, I'm going to call decode string, and I'm going to pass in the decoder, which in this case, is going to be int. Then I'm going to pass in the string I want to decode. In this case, it's going to be num.
Then let's pass the result of decoded into text. It failed. That's because text is expecting to get a string, and what we've passed in instead is a result string, int. We can solve this pretty easily by passing our decoded value into toString which is an Elm global function that'll turn it into a string before passing it along to text.
Here you'll see OK 4. That's because result is a type that looks like this. Result XA equals error X or OK A. Here result is a parameterized type, meaning that it can take two different types, depending upon the situation that you're working with.
In this case, we're going to be using error string, and OK int. Since our decoding succeeded, you see here that we've got OK4. Let's say we put in here S, and then try to recompile. We get an error, given invalid JSON, unexpected token S at position one.
That means that we took the branch of error from our result instead of the OK branch. Let's put things back how they were when they were working. This is fine and dandy but let's do something a little bit more complex.
Let's decode a list. Let's make a new constant, nums. Let's give it a whole slew of numbers here, one through five. We need a recipe that will tell Elm how to decode a list of integers, instead of just an integer.
We're in luck, because the JSON decode library has a built-in decoder called list. Down here, we can type numbers decoder, make that of type decoder list int. Remember, I'm just filling in types here so that the compiler can help me in case I mess up.
The definition is going to be numbers decoder equals list int. List is a function which takes in a decoder, and it returns a decoder that expects a list of the things that the other decoder was. In this case, we're making a decoder that decodes a list of integers.
Now down in our decoded statement, instead of passing in int num, let's pass in numbers decoder, nums, and run it. Looks like I haven't imported the decoder type yet. Let's do that at the top here, decoder.
It looks like I also forgot to update the type of my decoded constant. Let's fix that by wrapping our int in a list. We've successfully decoded a list of numbers from a string containing a list of numbers. Let's recap.
In order to decode JSON in Elm, we first have to define a recipe for the thing we want to decode. Then we have to actually run the decoder. We're using some built-in libraries, containing a function for decoding strings, and other decoders that are premade.
We can use these built-in decoders to decode things from strings, or we can compose them together to make more complex decoders. Then we can run them, and get back a result, which can either be an error, or it can be OK. We can do something with that result, in this case, rendering it to the screen.