Как написать JSON Format для объекта в библиотеке Java, который не имеет метода применения? - программирование
Подтвердить что ты не робот

Как написать JSON Format для объекта в библиотеке Java, который не имеет метода применения?

Я застрял в этой конкретной проблеме около недели, и я полагаю, что я собираюсь написать это как вопрос здесь, чтобы очистить мои мысли и получить некоторые рекомендации.

Итак, у меня есть класс case, который имеет 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

"Нет дескриптора Json deserializer для типа List [models.Request]. Попробуйте реализовать неявные Writes или Format для этого типа." Хорошо, это имеет смысл.

Таким образом, следуя Воспроизвести документацию здесь, я пытаюсь написать 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" ) вызывает у меня проблемы:

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._ уже так, как это говорится в документации! Этот парень столкнулся с той же проблемой, но импорт зафиксировал его для него! Так почему?! Я думал, что это может быть просто проблема 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


Я также пытаюсь изменить tacks и писать Write для этого вместо Format, без функции fancy new combinator (__), следуя оригинальному блогу публикация официальных документов основывается на /copy -ined 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

В этот момент я нахожусь здесь на своем пути, поэтому я просто возвращаюсь к остальной части моего умственного стопа и сообщаю из своей первой части кода



Моя полная благодарность любому, кто может вывести меня из моей страхи за кодирование

4b9b3361

Ответ 1

Это не обязательно функции apply или unapply, которые вам нужны. Это a) функция, которая строит любой тип, который вам нужен, с некоторыми параметрами, и b) функцию, которая превращает экземпляр этого типа в кортеж значений (обычно соответствующий входным параметрам).

Функции apply и unapply, которые вы получаете бесплатно с классом case Scala, просто делают это, поэтому удобно их использовать. Но вы всегда можете написать свой собственный.

Обычно вы можете сделать это с помощью анонимных функций:

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, который мешает вам писать такие форматы, как это, только с одним значением. Это ограничение объясняется здесь, в соответствии с этим ответом.

Для вас способ, который работает, будет выглядеть более сложным:

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