播放 - 使用 Generic Crud 自定义 Json
Play - Custom Json with Generic Crud
我的应用程序中有一个 Crud 控制器,它与 JsonInception 完美配合,但使用自定义 json 转换器失败。
按照给定的案例class:
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 无法找到任何隐式转换器。
所以我的通用 Crud 控制器是这样的:
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 通过 Whosebug 给我一些帮助 =)
更新
我几乎可以使用这种方法了:
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])
)
}
您好,我认为您的案例 class 有一个选项参数,您的 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 _))
这里有几个问题,但它们都与泛型无关,因为您正在处理具体类型 Validity
。
首先,使用 Reads
组合子的最后一个参数应该是 (Validity.apply _)
。您只能将 unlift
与 Writes
一起使用。
其次,组合器中的类型必须映射到您的 Validity
class.
中的类型
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 _)
Reads
已经存在 UUID
和 DateTime
,所以这应该没问题。
同样,Writes[Validity]
看起来像这样:
implicit val writes: Writes[Validity] = (
(JsPath \ "id").writeNullable[UUID] and
(JsPath \ "objectType").write[String] and
(JsPath \ "since").write[DateTime] and
(JsPath \ "until").write[DateTime] and
(JsPath \ "objectId").write[UUID] and
(JsPath \ "validityTargetId").write[UUID] and
(JsPath \ "validityTargetType").write[String]
)(unlift(Validity.unapply))
我的应用程序中有一个 Crud 控制器,它与 JsonInception 完美配合,但使用自定义 json 转换器失败。
按照给定的案例class:
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 无法找到任何隐式转换器。
所以我的通用 Crud 控制器是这样的:
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 通过 Whosebug 给我一些帮助 =)
更新
我几乎可以使用这种方法了:
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])
)
}
您好,我认为您的案例 class 有一个选项参数,您的 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 _))
这里有几个问题,但它们都与泛型无关,因为您正在处理具体类型 Validity
。
首先,使用 Reads
组合子的最后一个参数应该是 (Validity.apply _)
。您只能将 unlift
与 Writes
一起使用。
其次,组合器中的类型必须映射到您的 Validity
class.
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 _)
Reads
已经存在 UUID
和 DateTime
,所以这应该没问题。
同样,Writes[Validity]
看起来像这样:
implicit val writes: Writes[Validity] = (
(JsPath \ "id").writeNullable[UUID] and
(JsPath \ "objectType").write[String] and
(JsPath \ "since").write[DateTime] and
(JsPath \ "until").write[DateTime] and
(JsPath \ "objectId").write[UUID] and
(JsPath \ "validityTargetId").write[UUID] and
(JsPath \ "validityTargetType").write[String]
)(unlift(Validity.unapply))