1

I'm trying to use circe to decode a JSON object into a list of objects. I only want to use some of the fields of the JSON response to create the object, so I feel like I have to create a custom decoder.

The class I want to make a sequence of is defined as follows:

case class Review(Id: String, ProductId: String, Rating: Int)

I tried creating a custom decoder like this:

implicit val reviewDecoder: Decoder[Review] = Decoder.instance { c =>

  val resultsC = c.downField("Results")

  for {
    id <- resultsC.downArray.get[String]("Id")
    productId <- resultsC.downArray.get[String]("ProductId")
    rating <- resultsC.downArray.get[Int]("Rating")
  } yield Review(id, productId, rating)
}

reviewDecoder.decodeJson(json) seems to result in only doing the first result and not all of them.

I have a JSON response like this:

 {
   "Limit":2,
   "Offset":0,
   "TotalResults":31,
   "Locale":"en_US", 
   "Results":
   [
      {"Id":"14518388",
       "CID":"21a9436b",
       "ProductId":"Product11", 
       "AuthorId":"jcknwekjcnwjk",
       "Rating":3
      },
      {"Id":"14518035",
       "CID":"8d67b6f5",
       "ProductId":"Product11",
       "AuthorId":"fnkjwernfk",
       "Rating":3
      }
   ],
   "Includes":{},
   "HasErrors":false,
   "Errors":[]}

I want to be able to parse this JSON object using circe to create a Seq[Review], but I'm stumped how.

****Edit** Luis' answer does answer this question but say I have a more complicated class I want to create a sequence of:

case class User(id: Int)

case class Review(user: User, ProductId: String, Rating: Int)

How would I be able to create a sequence of Reviews in this case?

1 Answer 1

1

I would just use the cursor to getting the Array and then use the generic decoder.

The following code was tested on ammonite, where json is a string containing your sample input.

import $ivy.`io.circe::circe-core:0.11.1`
import $ivy.`io.circe::circe-generic:0.11.1`
import $ivy.`io.circe::circe-parser:0.11.1`

import io.circe.{Decoder, Jsom}
import io.circe.parser.parse

final case class Review(Id: String, ProductId: String, Rating: Int)
implicit val reviewDecoder: Decoder[Review] = io.circe.generic.semiauto.deriveDecoder

parse(json).getOrElse(Json.Null).hcursor.downField("Results").as[List[Review]]
// res: io.circe.Decoder.Result[List[Review]] = Right(List(Review("14518388", "Product11", 3), Review("14518035", "Product11", 3)))
Sign up to request clarification or add additional context in comments.

2 Comments

What if the names don't match one-to-one? Say the JSON response has a value called "productId" and I want to map it to the object member "pID", is there a way to specify which field should map to which?
Yes, you can. Take a look to the Documentation, go to the Custom key mappings via annotations section. (it is at the bottom).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.