【问题标题】:How do I write a JSON Format for an object in the Java library that doesn't have an apply method?如何为 Java 库中没有 apply 方法的对象编写 JSON 格式?
【发布时间】:2013-06-21 07:16:32
【问题描述】:

我已经被这个特殊问题困扰了大约一个星期,我想我将把这个问题写在这里作为一个问题来清理我的想法并获得一些指导。

所以我有一个具有java.sql.Timestamp 字段的案例类:

case class Request(id: Option[Int], requestDate: Timestamp)

我想把它转换成JsObject

val q = Query(Requests).list // This is Slick, a database access lib for Scala
  printList(q)    
  Ok(Json.toJson(q))   // and this is where I run into trouble

“没有找到类型 List[models.Request] 的 Json 反序列化器。尝试为此类型实现隐式写入或格式。”好吧,这是有道理的。

所以按照Play documentation here,我尝试写一个Format...

implicit val requestFormat = Json.format[Request]  // need Timestamp deserializer
implicit val timestampFormat = (
      (__ \ "time").format[Long]   // error 1
)(Timestamp.apply, unlift(Timestamp.unapply))  // error 2

错误 1

Description Resource Path Location Type overloaded method value format with alternatives:   

(w: play.api.libs.json.Writes[Long])(implicit r: play.api.libs.json.Reads[Long])play.api.libs.json.OFormat[Long] 
<and>   
(r: play.api.libs.json.Reads[Long])(implicit w: play.api.libs.json.Writes[Long])play.api.libs.json.OFormat[Long] 
<and>   
(implicit f: play.api.libs.json.Format[Long])play.api.libs.json.OFormat[Long]  
cannot be applied to (<error>, <error>)

显然像这样导入(请参阅documentation“ctrl+F 导入”)让我陷入困境:

import play.api.libs.json._    // so I change this to import only Format and fine
import play.api.libs.functional.syntax._
import play.api.libs.json.Json
import play.api.libs.json.Json._  

现在重载错误消失了,我遇到了更多问题:not found: value __ 我已经导入了.../functional.syntax._,就像它在文档中所说的那样! This guy 遇到了同样的问题,但导入为他解决了这个问题!所以为什么?!我认为这可能只是 Eclipse 的问题并尝试play run 无论如何......没有任何改变。美好的。编译器永远是对的。

导入play.api.lib.json.JsPath,把__改成JsPath,还有wallah:

错误 2

value apply is not a member of object java.sql.Timestamp
value unapply is not a member of object java.sql.Timestamp


我还尝试更改策略并为此编写一个写入而不是格式,没有花哨的新组合器 (__) 功能,遵循original blog post the official docs are based on/copy-pasted from

// I change the imports above to use Writes instead of Format
 implicit val timestampFormat = new Writes[Timestamp](  // ERROR 3
    def writes(t: Timestamp): JsValue = { // ERROR 4 def is underlined
      Json.obj(
          /* Returns the number of milliseconds since 
           January 1, 1970, 00:00:00 GMT represented by this Timestamp object. */
              "time" -> t.getTime() 
      )
    }
  )

错误 3:trait Writes is abstract, cannot be instantiated

错误 4:illegal start of simple expression

在这一点上,我已经不知所措了,所以我只是回到我的心理堆栈的其余部分,并从我的第一段代码报告



我非常感谢任何能让我摆脱编码痛苦的人

【问题讨论】:

    标签: java scala playframework playframework-2.0


    【解决方案1】:

    您需要的不一定是applyunapply 函数。它是 a) 一个函数,可以在给定一些参数的情况下构造您需要的任何类型,以及 b) 一个将该类型的实例转换为值元组的函数(通常与输入参数匹配。)

    您通过 Scala 案例类免费获得的 applyunapply 函数恰好就是这样做的,因此使用它们很方便。但你总是可以自己写。

    通常你可以使用匿名函数来做到这一点:

    import java.sql.Timestamp
    import play.api.libs.functional.syntax._
    import play.api.libs.json._
    
    implicit val timestampFormat: Format[Timestamp] = (
      (__ \ "time").format[Long]
    )((long: Long) => new Timestamp(long), (ts: Timestamp) => (ts.getTime))
    

    但是!在这种情况下,您违反了 API 的限制,该限制阻止您编写这样的格式,只有一个值。这个限制在here 解释,根据this answer

    对你来说,可行的方法是这个看起来更复杂的 hack:

    import java.sql.Timestamp
    import play.api.libs.functional.syntax._
    import play.api.libs.json._
    
    implicit val rds: Reads[Timestamp] = (__ \ "time").read[Long].map{ long => new Timestamp(long) }
    implicit val wrs: Writes[Timestamp] = (__ \ "time").write[Long].contramap{ (a: Timestamp) => a.getTime }
    implicit val fmt: Format[Timestamp] = Format(rds, wrs)
    
    // Test it...
    val testTime = Json.obj("time" -> 123456789)
    assert(testTime.as[Timestamp] == new Timestamp(123456789))
    

    【讨论】:

    • 现在开枪 我在尝试 toJson() 时遇到了 NullPointerException ... 而且我没有尝试序列化任何空值,即使这不会导致错误? ...
    • @MeredithLeu:确保如果您的 Reads/Writes/Formats 引用自身或稍后在具有循环依赖关系的同一对象中定义的其他 R/W/F,则使用 lazyFormat 帮助器.否则它可以编译但在运行时给出 NPE。我自己也遇到了这个问题。参见递归格式定义的处理here
    • 但是我的架构中没有任何循环依赖...我有其他对象的外键,但它们都没有指向。
    • 如果没有看到更多代码和堆栈跟踪(也许是另一个问题),很难提供帮助。Scala 中的 NPE 错误通常与变量在类或对象中初始化的顺序有关。如果您在初始化之前尝试使用一个(即在读取/写入/格式中),则该值将为 null 并且它将在运行时崩溃。
    • 是的,这就是我提到的初始化问题的顺序。您的请求格式是在时间戳格式之前生成的(神奇的是,由宏),这取决于它。我认为您可以将时间戳格式代码粘贴在 implicit val requestFormat = Json.format[Request] 行上方,如果没有 lazy 修饰符,它也可以工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-04
    • 1970-01-01
    • 2015-01-08
    • 2011-04-07
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多