如何在Play 2.1中将案例类序列化/反序列化为Json

时间:2013-02-23 15:34:25

标签: json scala playframework-2.0 playframework-2.1

我正在尝试将一些案例类序列化/反序列化为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]                        ^

我知道......只包含一个字符串的case类没有多大意义......但是我需要序列化/反序列化一个类似于上面描述的来自外部库的case类。< / p>

有什么想法吗?我错过了什么吗?任何帮助都会非常感激......我疯了:-(谢谢。

3 个答案:

答案 0 :(得分: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中的关键字,它不能用作参数名称(但我认为它仅适用于此示例)

编辑:播放2.3.X时尚未需要此解决方法。宏工作正常。

答案 1 :(得分: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的{​​{1}}函数(其中X是1到1) 22)。

为了提供类似的东西,你可以创建一个隐式类,它提供一个FunctionalBuilder#CanBuildX方法,其签名与那个很好的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))
}

答案 2 :(得分:0)

为什么不简单地在case类中添加一个未使用的字段。提出一个体面的评论或使用一个自我解释的字段名称。

//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 _)  
}