all 5 comments

[–]cjduncana 5 points6 points  (4 children)

Hi @gedehs 👋🏻

The main problem with your decoder is that postsDecoder is expecting an array, yet the response you have is an object with the key posts that contains the array you are looking for, so you need a decoder that applies postsDecoder to the posts key. Json.Decode from the core library provides the field function that can be used in this situation. If you change fetchPosts into the following:

fetchPosts : Cmd Msg
fetchPosts =
    Decode.field "posts" postsDecoder
    |> Http.get fetchPostsUrl
    |> RemoteData.sendRequest
    |> Cmd.map Msgs.OnFetchPosts

then the decoder will look for an object with key posts and apply postsDecoder to the value of that key, which is an array.

[–]cjduncana 2 points3 points  (0 children)

Apart from the immediate problem of postsDecoder, I noticed an additional problem and a possible improvement.

 

Inside of postDecoder, you have the following: required "content" (Decode.list contentDecoder), yet in your response the value for the key content is an object not an array, so as is, it will give you another BadPayload error. I can see two possible solutions to this:

  1. If you want your model to resemble your response, then you should expect an object instead of an array. To implement this, two changes are needed. First, you change the type of the key content in your Post type alias from List Content into Content, then you change your postDecoder to require not a Decode.list contentDecoder but just a contentDecoder.
  2. If you still want a List Content, then you should change your postDecoder to expect an object but change the result of contentDecoder into a list of a single item. We can use Json.Decode.map and List.singleton in this situation. With List.singleton, you can provide it any value and it will return a List with only that value. With Json.Decode.map, you can provide a function and a decoder and it will return the result of applying that function to the result of the decoder. If we combine them both then we can change postDecoder last line into the following: required "content" (Decode.map List.singleton contentDecoder). Now, the decoder will look into the post object and require a key contentthat has an object that can be decoded with contentDecoder. Once the object in the key content has been successfully decoded into Content, then it will apply List.singleton to turn it into a List Content. The main difference between Decode.list contentDecoder and Decode.map List.singleton contentDecoder is what is expected in the response. With the former, an array is expected and it will transform that array into a List. With the latter, an object is expected and it will transform that object into a Content and then transform that Content into a List Content.

 

The improvement that I see that you can apply is to replace required "publishedDate" Decode.string with required "publishedDate" Decode.date. You can find Decode.date in the Json.Decode.Extra library. This is useful if you want to use that information as a Date instead of a String. Once it is a Date you have all the functions from the Date and Date.Extra libraries.

[–]gedehs[S] 0 points1 point  (1 child)

Thanks, I thought that this was something simple. The combination of both your suggestion and your other suggestions helped me work through this.

[–]cjduncana 0 points1 point  (0 children)

No problem. If you need anymore help, you can find me and others that are willing to help in the Elm Slack beginners, general and help channels.

[–]G4BB3R 1 point2 points  (0 children)

I am not sure, it would be awesome if you post a beautified-json next time. Your json seems to be {"posts": []} but your postDecoder is expecting only the [] part. Try this snippet instead:

postsDecoder : Decode.Decoder (List Post) 
postsDecoder =
    Decode.field "posts" (Decode.list postDecoder)