BoumTAC BoumTAC - 2 months ago 44
JSON Question

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

Answer

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.object3.

import Json.Decode as Json exposing (..)
import Json.Decode.Extra exposing (lazy)

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
    }

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):

emptyStringToListDecoder : Decoder (List a)
emptyStringToListDecoder =
  string `andThen` \s ->
    case s of
      "" -> succeed []
      _ -> fail "Expected an empty string"

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

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

(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)