我的应用程序中有一个Crud控制器,它与JsonInception完美配合,但是使用自定义json转换器失败了。
按照给定的案例类:
case class Validity(id: Option[UUID], objectType: String, since: DateTime, until: DateTime, objectId: UUID, validityTargetId: UUID, validityTargetType: String)
我有一个对象伴侣如下:
object Validity extends CrudObject[Validity] {
implicit val reads = Json.reads[Validity]
implicit val writes = Json.writes[Validity]
}
我的CrudObject是具有给定代码的特征:
trait CrudObject[T] {
val reads: Reads[T]
val writes: Writes[T]
}
这是必要的,因为我正在使用Generic Crud。没有这个,Play无法找到任何隐式转换器。
所以我的Generic Crud Controller就是这样的:
trait CrudController[T] extends Controller {
def service: ServiceModule[T]
def companion: CrudObject[T]
def search...
def insert...
implicit def reads: Reads[T] = companion.reads
implicit def writes: Writes[T] = companion.writes
对于每个控制器,我都有以下内容:
object ValidityController extends CrudController[Validity] {
override def service: GenericServiceModule[Validity] = ServiceModule
override def companion: CrudObject[Validity] = Validity
}
好的,正如我所说的那样,考虑到这种设计,完美无缺,我需要设计一个自定义的json转换器。是的,我知道,这很简单,但事情正在发生,我不知道如何摆脱它。
现在我正在尝试执行以下操作:
implicit val reads: Reads[Validity] = (
(JsPath \ "id").read[String] and
(JsPath \ "objectType").read[String] and
(JsPath \ "since").read[Long] and
(JsPath \ "until").read[Long] and
(JsPath \ "objectId").read[String] and
(JsPath \ "validityTargetId").read[String] and
(JsPath \ "validityTargetType").read[String]
)(unlift(Validity.apply _))
它给了我:
Type mismatch, expected: (NotInferedA) => Option[NotInferedB], actual: (CrudObject[Nothing]) => CrudObject[Nothing]
我相信这是发生的事情因为CrudObject是一个特质而且没有申请和取消申请。
无论如何,删除CrudObject会给我一个类似的错误:
Type mismatch, expected: (NotInferedA) => Option[NotInferedB], actual: (Option[UUID], String, DateTime, DateTime, UUID, UUID, String) => Validity
但即使我能解决这个问题,我也无法想象因为我的GenericCrud而没有CrudObject。
有什么想法吗?
PS:感谢@ m-z,他通过stackoverflow给我一些帮助=)
更新
我差不多采用这种方法:
implicit object validityFormat extends Format[Validity] {
override def writes(o: Validity): JsValue = {
Json.obj(
"id" -> JsString(o.id.getOrElse(null).toString),
"objectType" -> JsString(o.objectType),
"since" -> JsString(o.since.toString),
"until" -> JsString(o.since.toString),
"objectId" -> JsString(o.objectId.toString),
"validityTargetId" -> JsString(o.validityTargetId.toString),
"validityTargetType" -> JsString(o.validityTargetType))
}
override def reads(json: JsValue): JsResult[Validity] = {
JsSuccess(Validity(
(json \ "id").as[Option[UUID]],
(json \ "objectType").as[String],
(json \ "since").as[DateTime],
(json \ "until").as[DateTime],
(json \ "objectId").as[UUID],
(json \ "validityTargetId").as[UUID],
(json \ "validityTargetType").as[String])
)
}
}
使用这种方式,这与文档Scala Combinators不同,我没有得到上一个错误,这是好的,没有类型不匹配=)
现在我正在研究如何转换为UUID和DateTime。
更新
我做了一个有效的例子,但我接受了@ m-z答案,因为它的样板比我的少,但两者都运行良好。最大的区别是,在我的方法中,我需要为DateTime和UUID提供一些自定义转换器,而@ m-z方法则不需要!
implicit object UUIDFormatter extends Format[UUID] {
override def reads(json: JsValue): JsResult[UUID] = {
val uuid = json.validate[String]
JsSuccess(UUID.fromString(uuid.get))
}
override def writes(o: UUID): JsValue = {
JsString(o.toString)
}
}
implicit object DateTimeFormatter extends Format[DateTime] {
override def reads(json: JsValue): JsResult[DateTime] = {
val datetime = json.validate[Long]
JsSuccess(new DateTime(datetime.get))
}
override def writes(o: DateTime): JsValue = {
JsNumber(o.getMillis)
}
}
implicit object validityFormat extends Format[Validity] {
override def writes(o: Validity): JsValue = {
Json.obj(
"id" -> JsString(o.id.getOrElse(null).toString),
"objectType" -> JsString(o.objectType),
"since" -> JsNumber(o.since.getMillis),
"until" -> JsNumber(o.since.getMillis),
"objectId" -> JsString(o.objectId.toString),
"validityTargetId" -> JsString(o.validityTargetId.toString),
"validityTargetType" -> JsString(o.validityTargetType))
}
override def reads(json: JsValue): JsResult[Validity] = {
JsSuccess(Validity(
(json \ "id").as[Option[UUID]],
(json \ "objectType").as[String],
(json \ "since").as[DateTime],
(json \ "until").as[DateTime],
(json \ "objectId").as[UUID],
(json \ "validityTargetId").as[UUID],
(json \ "validityTargetType").as[String])
)
}
答案 0 :(得分:2)
这里有几个问题,但它们都与泛型无关,因为你正在处理具体类型Validity
。
首先,使用Reads
组合子的最后一个参数应为(Validity.apply _)
。您只能将unlift
与Writes
一起使用。
其次,组合器中的类型必须映射到Validity
类中的类型。
implicit val reads: Reads[Validity] = (
(JsPath \ "id").readNullable[UUID] and // readNullable reads to Option
(JsPath \ "objectType").read[String] and
(JsPath \ "since").read[DateTime] and
(JsPath \ "until").read[DateTime] and
(JsPath \ "objectId").read[UUID] and
(JsPath \ "validityTargetId").read[UUID] and
(JsPath \ "validityTargetType").read[String]
)(Validity.apply _)
{p {1}}和Reads
已经存在 UUID
,所以这应该可行。
同样,DateTime
看起来像这样:
Writes[Validity]
答案 1 :(得分:1)
嗨我认为您的案例类有一个选项参数,您的Reads必须在可选参数中具有readnullable必须是这样的,我不确定UIID和String
case class Validity(id: Option[UUID], objectType: String, since: DateTime, until: DateTime, objectId: UUID, validityTargetId: UUID, validityTargetType: String)
implicit val reads: Reads[Validity] = (
(JsPath \ "id").readNullable[UIID] and
(JsPath \ "objectType").read[String] and
(JsPath \ "since").read[Long] and
(JsPath \ "until").read[Long] and
(JsPath \ "objectId").read[String] and
(JsPath \ "validityTargetId").read[String] and
(JsPath \ "validityTargetType").read[String]
)(unlift(Validity.apply _))