ELM parse nested json
Asked Answered
N

1

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

Negate answered 17/9, 2016 at 22:30 Comment(1)
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 package.elm-lang.org/packages/elm-lang/core/latest/…. 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.Photoemission
M
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)

Matty answered 18/9, 2016 at 12:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.