Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ELM parse nested json

I have a json array with multiple comments which can be nested.

exemple:

[
  {
    "author": "john",
    "comment" : ".....",
    "reply": "",
  },
  {
    "author": "Paul",
    "comment" : ".....",
    "reply": [  
      {
        "author": "john",
        "comment" : "nested comment",
        "reply": [
          {
            "author": "Paul",
            "comment": "second nested comment"
          }
        ]
      },
      {
        "author": "john",
        "comment" : "another nested comment",
        "reply": ""
      }
    ]
  },
  {
    "author": "Dave",
    "comment" : ".....",
    "reply": ""
  },
]

So it's a list of comment, which every comment can have a reply with an infinite number of reply. With Json.Decode.list I can decode the first level of comment, but how do I checked if there is some reply and then parse again ?

This is a simplify version of what I'm try to do. I'm actually trying to decode reddit comments. exemple

like image 490
BoumTAC Avatar asked Sep 17 '16 22:09

BoumTAC


1 Answers

Elm won't let you create a recursive record type alias, so you'll have to use a union type for Customer. You may also want a convenience function for creating a user so you can use Json.map3.

Your example json has an oddity: Sometimes reply is an empty string and sometimes it's a list. You'll need a special decoder to turn that string into an empty list (assuming an empty list is synonymous with an empty list in this context).

Since you have a recursive type, you need to use lazy for decoding the child comments to avoid a runtime error.

import Html exposing (Html, text)
import Json.Decode as Json exposing (..)


main : Html msg
main =
    text <| toString <| decodeString (list commentDecoder) s


type Comment
    = Comment
        { author : String
        , comment : String
        , reply : List Comment
        }


newComment : String -> String -> List Comment -> Comment
newComment author comment reply =
    Comment
        { author = author
        , comment = comment
        , reply = reply
        }


emptyStringToListDecoder : Decoder (List a)
emptyStringToListDecoder =
    string
        |> andThen
            (\s ->
                case s of
                    "" ->
                        succeed []

                    _ ->
                        fail "Expected an empty string"
            )


commentDecoder : Decoder Comment
commentDecoder =
    map3 newComment
        (field "author" string)
        (field "comment" string)
        (field "reply" <|
            oneOf
                [ emptyStringToListDecoder
                , list (lazy (\_ -> commentDecoder))
                ]
        )


s =
    """
[{
  "author": "john",
  "comment": ".....",
  "reply": ""
}, {
  "author": "Dave",
  "comment": ".....",
  "reply": ""
}, {
  "author": "Paul",
  "comment": ".....",
  "reply": [{
    "author": "john",
    "comment": "nested comment",
    "reply": [{
      "author": "Paul",
      "comment": "second nested comment",
      "reply": ""
    }]
  }, {
    "author": "john",
    "comment": "another nested comment",
    "reply": ""
  }]
}]
"""

(Your json is also a little off in other ways: There are a few extra commas after the last parts of list and one of the reply fields is missing)

like image 104
Chad Gilbert Avatar answered Oct 25 '22 00:10

Chad Gilbert