14

我正在尝试将一些案例类序列化/反序列化到/来自 Json ......并且在处理只有一个字段的案例类时遇到了麻烦(我使用的是 Play 2.1):

import play.api.libs.json._
import play.api.libs.functional.syntax._

case class MyType(type: String)

object MyType {

  implicit val myTypeJsonWrite = new Writes[MyType] {
    def writes(type: MyType): JsValue = {
      Json.obj(
        "type" -> MyType.type
      )
    }
  }

  implicit val myTypeJsonRead = (
    (__ \ 'type).read[String]
  )(MyType.apply _)
}

上面的代码总是生成以下错误消息:

[error] /home/j3d/Projects/test/app/models/MyType.scala:34: overloaded method value read with alternatives:
[error]   (t: String)play.api.libs.json.Reads[String] <and>
[error]   (implicit r: play.api.libs.json.Reads[String])play.api.libs.json.Reads[String]
[error]  cannot be applied to (String => models.MyType)
[error]     (__ \ 'method).read[String]
[error]                        ^

我知道......一个只包含一个字符串的案例类没有多大意义......但我需要序列化/反序列化一个与我上面描述的来自外部库的案例类非常相似的案例类。

任何的想法?我错过了什么吗?任何帮助将不胜感激......我快疯了:-(谢谢。

4

3 回答 3

23

Json 组合器不适用于 Play 2.1 中的单字段案例类(在 2.2 中应该可以)

Pascal(此 API 的作者)在这里解释了这种情况https://groups.google.com/forum/?fromgroups=#!starred/play-framework/hGrveOkbJ6U

有一些可行的解决方法,例如:

case class MyType(value: String)
val myTypeRead = (__ \ 'value).read[String].map(v => MyType(v)) // covariant map

ps:type是 Scala 中的关键字,不能用作参数名称(但我假设仅用于此示例)

编辑:play 2.3.X 还不需要这个解决方法。宏工作正常。

于 2013-02-23T17:04:54.270 回答
4

问题是(据我所知)Play 2.1 框架仅处理从Tuple2. 在示例中,它是这样使用的:

case class CaseClass(key1: String, key2: String)
object CaseClass {
  implicit val caseClassFormat = {
    val jsonDescription =
      (__ \ "key1").format[String] and (__ \ "key2").format[String]

    jsonDescription(CaseClass.apply _, unlift(CaseClass.unapply))
  }
}

然后使用它

val caseClassJson = Json.toJson(CaseClass("value1", "value2"))

println(caseClassJson)
println(Json.fromJson[CaseClass](caseClassJson))

在您的情况下,您不能使用该and方法(您只有一个值),因此无法访问那个不错的apply函数FunctionalBuilder#CanBuildX(其中 X 是 1 到 22)。

为了提供类似的东西,您可以创建一个隐式类,该类提供一个build与那个不错的方法具有相似签名的apply方法

implicit class FormatBuilder[M[_], A](o: M[A]) {
  def build[B](f1: A => B, f2: B => A)(implicit fu: InvariantFunctor[M]) =
    fu.inmap[A, B](o, f1, f2)
}

现在你可以像这样调整你的案例类

case class MyType(tpe: String)

object MyType {
  implicit val myTypeFormat =
    ((__ \ "type").format[String]) build (MyType.apply _, unlift(MyType.unapply))
}

然后你可以像这样使用它

val myTypeJson = Json.toJson(MyType("bar"))

println(myTypeJson)
println(Json.fromJson[MyType](myTypeJson))
于 2013-02-23T17:13:28.717 回答
0

为什么不简单地将未使用的字段添加到案例类。发表体面的评论或使用不言自明的字段名称。

//f2 is unused field for de/serialization convenience due to limitation in play

case class SingleField(f1: String, f2: Option[String]) 
object SingleField {
   implicit val readSingleField : Reads[SingleField] = (
        (__ \ "f1").read[String] and 
           (__ \ "f2").readNullable[String])(SingleField.apply _)  
}
于 2017-08-24T09:13:28.133 回答