Last active
November 26, 2020 04:40
-
-
Save supermacro/5b38c807a29a79e49795372960149ad8 to your computer and use it in GitHub Desktop.
Nested Comment Tree Decoder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Main exposing (main) | |
import Browser | |
import Html exposing (Html, div, text) | |
import Json.Decode as D exposing (Decoder) | |
{- | |
This module demonstrates how to decode a nested tree of nodes that are NOT infinitely recursive. | |
-} | |
type alias CommentTree = | |
CommentWithReplies | |
(CommentWithReplies | |
LeafComment | |
) | |
type alias CommentWithReplies a = | |
{ id : String | |
, anonymousAuthorName : String | |
, replies : List a | |
, body : String | |
} | |
type alias LeafComment = | |
{ id : String | |
, anonymousAuthorName : String | |
, body : String | |
} | |
apiResponseDecoder : Decoder a -> Decoder a | |
apiResponseDecoder dataDecoder = | |
D.field "data" dataDecoder | |
-- intentionally ommiting type here | |
commonFields = | |
{ id = D.field "id" D.string | |
, anonymousAuthorName = D.field "anon_author_name" D.string | |
} | |
leafCommentDecoder : Decoder LeafComment | |
leafCommentDecoder = | |
D.map2 LeafComment | |
commonFields.id | |
commonFields.anonymousAuthorName | |
makeCommentTreeDecoder : Decoder a -> Decoder (CommentWithReplies a) | |
makeCommentTreeDecoder nextDecoder = | |
D.map3 CommentWithReplies | |
commonFields.id | |
commonFields.anonymousAuthorName | |
(D.field "replies" <| D.list nextDecoder) | |
commentListDecoder : Decoder (List CommentTree) | |
commentListDecoder = | |
let | |
commentTreeDecoder = | |
makeCommentTreeDecoder | |
(makeCommentTreeDecoder leafCommentDecoder | |
) | |
in | |
D.field "comments" | |
(D.list commentTreeDecoder) | |
json : String | |
json = """ | |
{ | |
"data": { | |
"comments": [ | |
{ | |
"id": "ckhxpcv2l0056b7t28lc2qnii", | |
"anon_author_name": "unbeatable-caped-terror", | |
"body": "A top-level comment", | |
"votes": 0, | |
"post_id": "ckhxpc7sh0032b7t24cmlli2j", | |
"created_at": 1606326555981, | |
"updated_at": 1606326555981, | |
"parent_comment_id": null, | |
"author_id": null, | |
"author": null, | |
"replies": [] | |
}, | |
{ | |
"id": "ckhxpde9v0069b7t2tuddh7lm", | |
"anon_author_name": "infamous-sir-master", | |
"body": "Another top-level comment", | |
"votes": 0, | |
"post_id": "ckhxpc7sh0032b7t24cmlli2j", | |
"created_at": 1606326580868, | |
"updated_at": 1606326580868, | |
"parent_comment_id": null, | |
"author_id": null, | |
"author": null, | |
"replies": [ | |
{ | |
"id": "ckhxpdorx0077b7t2y28g02dj", | |
"anon_author_name": "criminal-tomorrow-storm", | |
"body": "Yo this is a reply", | |
"votes": 0, | |
"post_id": "ckhxpc7sh0032b7t24cmlli2j", | |
"created_at": 1606326594477, | |
"updated_at": 1606326594478, | |
"parent_comment_id": "ckhxpde9v0069b7t2tuddh7lm", | |
"author_id": null, | |
"author": null, | |
"replies": [] | |
} | |
] | |
} | |
], | |
"siteVerified": false, | |
"postId": "ckhxpc7sh0032b7t24cmlli2j", | |
"leafIds": [ | |
"ckhxpcv2l0056b7t28lc2qnii", | |
"ckhxpdorx0077b7t2y28g02dj" | |
] | |
} | |
} | |
""" | |
errorToHtml : D.Error -> Html.Html msg | |
errorToHtml error = | |
Html.pre [] [ Html.text (D.errorToString error) ] | |
decoded : Html msg | |
decoded = | |
let | |
decoder = apiResponseDecoder commentListDecoder | |
in | |
case D.decodeString decoder json of | |
Ok data -> | |
text "Yay! Successfully parsed a comment tree" | |
Err e -> | |
errorToHtml e | |
main : Program () () Never | |
main = | |
Browser.sandbox | |
{ init = () | |
, view = \_ -> div [] [ decoded ] | |
, update = \_ _ -> () | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment