7

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

BoumTAC
  • 3,531
  • 6
  • 32
  • 44
  • you don't want to have a mental model of parsing "again". you want to work with intermediate values or different decoders for the same field. have a look at http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Decode#andThen. it allows to switch sub-decoders depending on already decoded data. also there is `oneOf`, allowing you to either parse the empty strings (which you could manually convert to an empty list of replies) OR the nested replies. – pierrebeitz Sep 18 '16 at 11:50

1 Answers1

7

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)

rofrol
  • 14,438
  • 7
  • 79
  • 77
Chad Gilbert
  • 36,115
  • 4
  • 89
  • 97