【问题标题】:How to serialize/deserialize case classes to/from Json in Play 2.1如何在 Play 2.1 中序列化/反序列化与 Json 之间的案例类
【发布时间】:2013-02-09 03:14:13
【问题描述】:

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

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

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

【问题讨论】:

标签: json scala playframework-2.0 playframework-2.1


【解决方案1】:

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 中的关键字,不能用作参数名称(但我假设它只是用于此示例)

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

【讨论】:

  • 您可以在 JSON 序列化案例类中捕获 type,方法是将字段名称包装在反引号中。我不知道如何在降价中向您展示这一点,因为反引号是代码块:)
  • 这应该可以工作:`type` — 只需用反斜杠转义它们,如下所示:`\`type\``
  • 我使用的是 Play 2.3 (Scala),我还需要解决这个限制。我认为它还没有修复。
  • 我刚刚使用 Play 2.3.8 进行了测试,它适用于单字段案例类。我还鼓励您查看这个库 (github.com/jto/validation),它具有更多关于 Json/Xml/Form 验证的功能。
  • @JulienLafont 您能否发布您的工作 2.3.8 示例的要点?像 Felipe 一样,如果没有解决方法,我也会在 2.3.8 中遇到错误。
【解决方案2】:

问题是(据我所知)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 方法(您只有一个值),因此无法访问 FunctionalBuilder#CanBuildX 的漂亮 apply 函数(其中 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))

【讨论】:

  • EECOLOR 给出的解决方案归结为使用“inmap”,它将Format[A] 转换为Format[B]。简洁地记录了here。您也可以直接使用inmap,但 EECOLOR 的解决方案干净利落地扩展了 JSON 组合器。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 2014-07-12
    • 2011-06-29
    • 1970-01-01
    • 2017-04-10
    • 2013-08-04
    • 2023-03-10
    • 2013-08-12
    • 2021-06-27
    • 1970-01-01
    相关资源
    最近更新 更多