如何为 Java 库中没有 apply 方法的对象编写 JSON 格式?

我已经在这个特定的问题上呆了大约一个星期了,我想我要把它写成一个问题,以清除我的想法并获得一些指导。

所以我有一个案例类,它有一个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 找到 Json 反序列化程序。请求]。尝试为此类型实现隐式写入或格式。好吧,这是有道理的。

因此,按照此处的Play文档,我尝试编写一个格式...

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

显然,像这样导入(请参阅文档“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._  

现在重载错误消失了,我到达了更多的废话:我已经导入了,就像文档中所说的那样!这家伙遇到了同样的问题,但进口为他修复了它!为什么呢?!我认为这可能只是Eclipse的问题,并试图无论如何......没有任何变化。好。编译器始终是正确的。not found: value __.../functional.syntax._play run

Imported play.api.lib.json.JsPath,更改为 和 wallah:__JsPath

错误 2

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


我还尝试更改钉子并为此编写一个写而不是格式,没有花哨的新组合器()功能,方法是遵循官方文档基于/复制粘贴的原始博客文章__

// I change the imports above to use Writes instead of Format
 implicit val timestampFormat = new Writes1661194656(  // 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

在这一点上,我即将在这里结束我的智慧,所以我只是回到我的心理堆栈的其余部分,并从我的第一段代码中报告。



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


答案 1

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

使用 Scala case 类免费获得的 和 函数恰好可以做到这一点,因此使用它们很方便。但你总是可以写自己的。applyunapply

通常,您可以使用匿名函数执行此操作,如下所示:

import java.sql.Timestamp
import play.api.libs.functional.syntax._
import play.api.libs.json._

implicit val timestampFormat: Format1661194656 = (
  (__ \ "time").format[Long]
)((long: Long) => new Timestamp(long), (ts: Timestamp) => (ts.getTime))

然而!在这种情况下,您违反了API的限制,该限制阻止您编写这样的格式,只有一个值。根据此答案此处将对此进行解释。

对于您来说,一种可行的方法是这种看起来更复杂的黑客:

import java.sql.Timestamp
import play.api.libs.functional.syntax._
import play.api.libs.json._

implicit val rds: Reads1661194656 = (__ \ "time").read[Long].map{ long => new Timestamp(long) }
implicit val wrs: Writes1661194656 = (__ \ "time").write[Long].contramap{ (a: Timestamp) => a.getTime }
implicit val fmt: Format1661194656 = Format(rds, wrs)

// Test it...
val testTime = Json.obj("time" -> 123456789)
assert(testTime.as1661194656 == new Timestamp(123456789))

答案 2

推荐