samba
samba

Reputation: 3101

Scala - How to implement implicit JSON reader

I've implemented implicit Json Reads in order to read two fields from JSON, saleId and saleType. I wanted to make getSales method return a tuple (Int, String) representing saleId and saleType accordingly. But when I call the getSales method I'm getting the following errors:

Error:(46, 79) No JSON deserializer found for type (Int, String). Try to implement an implicit Reader or JsonFormat for this type.
    val salesData = (salesJson \ "sales").as[(Int, String)]

Error:(46, 79) not enough arguments for method as: (implicit reader: org.json4s.Reader[(Int, String)], implicit mf: Manifest[(Int, String)])(Int, String).
Unspecified value parameters reader, mf.
    val salesData = (salesJson \ "sales").as[(Int, String)]

I have implemented implicit Json Reads so really confused with the first error. Here is my implementation:

def getsales(context: SparkContext, saleId: Int): (Int, String)= {
    val url= buildUrl(context, saleId)

    implicit val salesReader: Reads[(Int, String)] = (
      (__ \ "id_from_API").read[Int] and
        (__ \ "sale_type").read[String]
      ).tupled

    val salesJson: JValue = parse(httpStringResponse(url, context))
    val salesData = (salesJson \ "sales_stats").as[(Int, String)]

    salesData
}

Upvotes: 0

Views: 1441

Answers (1)

étienne
étienne

Reputation: 240

Two notes concerning you code:

val salesData = (salesJson \ "sales").as[(Int, String)]
val salesData = (salesJson \ "sales_stats").as[(Int, String)]

might have to be the same.

Instead of JValue you might have wanted to put JsValue in the line

val salesJson: JValue = parse(httpStringResponse(url, context))

Other than that testing your JSON reader code separately from the rest of your code might be helpful.

The following worked for me:

import org.scalatest.WordSpec
import play.api.libs.functional.syntax._
import play.api.libs.json._

class ReadsExample extends WordSpec {

  "read" in {
    val sales =
      """
          {
            "sales_stats": {
            "id_from_API": 42,
            "sale_type": "cheap"
          }
        }
        """.stripMargin

     implicit val salesReader: Reads[(Int, String)] = (
      (JsPath \ "id_from_API").read[Int] and
        (JsPath \ "sale_type").read[String]
      ).tupled

    val salesJson: JsValue = Json.parse(sales)
    val salesData = (salesJson \ "sales_stats").as[(Int, String)]
 }

}

Please note that the version of play-json used here is 2.3.10.

EDIT

code example to the question in the comment

import org.scalatest.WordSpec
import play.api.libs.json.Json.reads
import play.api.libs.json.{Json, _}

class ReadsExample extends WordSpec {

  "read" in {
    val sales =
      """
          {
            "id_from_API": 9,
            "sale_type": {
              "main" : "a",
              "sub" : "b"
          }
      }
    """.stripMargin

    val salesJson: JsValue = Json.parse(sales)
    val salesData = salesJson.as[Sales]
  }

}

case class Sales(id_from_API: Int, sale_type: SaleType)
case class SaleType(main: String, sub: String)

object Sales {
  implicit val st: Reads[SaleType] = reads[SaleType]
  implicit val of: Reads[Sales] = reads[Sales]
}

Upvotes: 1

Related Questions