Scala - 如何实现隐式 JSON reader

Scala - How to implement implicit JSON reader

我实施了隐式 Json 读取,以便从 JSON 读取两个字段,saleId 和 saleType。我想使 getSales 方法 return 相应地表示 saleId 和 saleType 的元组 (Int, String)。但是当我调用 getSales 方法时,出现以下错误:

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)]

我已经实现了隐式 Json 读起来真的和第一个错误混淆了。这是我的实现:

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
}

关于您的代码的两个注意事项:

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

可能必须相同。

您可能希望将 JsValue 放在行中而不是 JValue

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

除此之外,将您的 JSON reader 代码与其余代码分开测试可能会有所帮助。

以下对我有用:

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)]
 }

}

请注意这里使用的play-json版本是2.3.10.

编辑

评论中问题的代码示例

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]
}