Join egghead, unlock knowledge.

Want more egghead?

This lesson is for members. Join us? Get access to all 3,000+ tutorials + a community with expert developers around the world.

Unlock This Lesson

Already subscribed? Sign In


    Decode a JSON Category Tree or Navigation Tree into Elm

    Enrico BuonannoEnrico Buonanno

    Some of our JSON data may have a recursive structure, especially trees such as the navigation trees or category trees that feature on many websites. When writing a decoder for a type with a recursive structure, you need to use the lazy function, to avoid defining a decoder in terms of itself, which would cause an infinite loop.

    This lesson assumes you're familiar with recursively defined union types in Elm.



    Become a Member to view code

    You must be a Member to view code

    Access all courses and lessons, track your progress, gain confidence and expertise.

    Become a Member
    and unlock code for this lesson


    Instructor: 00:01 Let's look at the decoding a tree structure. Here, I have this JSON structure that represents a category tree, and the structure of this JSON is it follows. You have an object that has a value that's a string, and then children which is at list of such values.

    00:18 Each child also has a value and some more children, it's category tree as you would find on many websites. How can we grow about decode in this? Firstly, let me write a type to represent this in elm. We'll define a type node and each node has a value, that's a string, that's a text that we want to be displayed with each node. Then, it has a list of nodes which had a children.

    00:48 You can clearly see that this is a recursively defined type. Let me now write a decoder for node. Let me call this node. Each node has two fields, the value and the children, I'm going to use map2 with node as the function to construct a node. Now, I need the two fields.

    01:10 The first field is value, and that's of type string. The second field is called children, and what's the type for that? That's a list of nodes. Let me try list node. Then, I would like to decode my JSON with decode string and using nodes as the decoder.

    01:41 Let me try saving that. You can see we get an error. It says, "Node is defined internal with self causing an infinity loop." Indeed, you can see node is defined in terms of node here. To get around this problem, JSON.decode exposes a dedicated function that is called lazy.

    02:01 Rather than just writing node recursively, you will write lazy and lazy takes an anonymous function where the first argument is disregarded, and we'll return node as decoder. You can see that this not compiles, but when we run the decoder we get an error, and that's because we were saying that a node always has a field called children, whereas in fact, our leaves only have a value, but don't have children.

    02:29 As we generally do with in all above fields, we use Maybe. Now as we use Maybe, our children would be a list wrapped into a Maybe. Then, what we'll do is we'd map Maybe with default and to list. If the children field is not found, then the Maybe will transform it into nothing, and then we'll use empty list as the default.

    02:57 I just need an extra set of parentheses here. This not seems to work and our decoding went OK, as we can see. Per let me try to create a more intuitive representation of this tree. Let me write a function render that takes a list of nodes, and we'll say, if that's an empty list, then just render an empty text block. Otherwise, create a numbered list. Then, for each node in our list of nodes, render the node, so let me define a function that we'll render a single node.

    03:43 Here, I'm going to destructure the node with its value and its children, and then render node will be a list item with two children, the value rendered as text. I'll recursively render the children. Here, let me use pattern matching, as well. We'll decode the JSON as a node.

    04:16 If that's an OK with a node with the value and some children, then I'm actually going to disregard the root value, because that's just the root, and then I'll render the children. If it's an error with some text, then I'll just render that error as text.

    04:43 Now, you can see we have some nested lists that express the structure of our tree. You could look at the JSON and walk through the children and see that the structure of the tree actually matches with the structure of the JSON.