玩 - 通用Crud自定义Json [英] Play - Custom Json with Generic Crud
问题描述
在我的应用程序中有一个Crud控制器,它可以与JsonInception完美配合使用,但是会失败,只能使用自定义的json转换器。
请遵循给定的案例类:
case类型有效性(id:Option [UUID],objectType:String,因为:DateTime,直到:DateTime,objectId:UUID,validityTargetId:UUID ,validityTargetType:String)
我有一个对象伴侣如下:
object有效性扩展了CrudObject [有效性] {
隐式val读取= Json.reads [有效期]
隐式val写入= Json.writes [有效性]
}
其中我的CrudObject是具有给定代码的特征:
trait CrudObject [T] {
val读取:读取[T]
val写入:写入[T]
}
因为我在Generic Crud中工作,所以这是必需的。如果没有这个,Play无法找到任何隐式转换器。
所以我的通用Crud控制器就是这样的:
trait CrudController [T]扩展控制器{
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
对于每个控制器,我有以下内容:
对象ValidityController extends CrudController [有效性] {
覆盖def service:GenericServiceModule [有效期] = ServiceModule
覆盖def伴随:CrudObject [有效期] =有效性
}
好吧,正如我所说的,完美的是,我需要设计一个定制的json转换器。是的,我知道,这很简单,但有些事情正在发生,我不知道如何摆脱它。
现在我正在尝试执行以下操作:
隐式val读取:读取[Validity] =(
(JsPath \id)。read [ (JsPath \objectType)。read [String]和
(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 _))
类型不匹配,预期:(NotInferedA)=>选项[NotInferedB],实际:(CrudObject [Nothing])=> CrudObject [Nothing]
我相信这是发生的事情cuz CrudObject是一个特征,并且不适用和不适用。
无论如何,删除CrudObject给了我一个类似的错误:
类型不匹配,预期:(NotInferedA)=> Option [NotInferedB],actual:(Option [UUID],String,DateTime,DateTime,UUID,UUID,String)=>有效性
但即使我能解决这个问题,我也无法想象没有CrudObject因为我的GenericCrud而生活。
任何想法?
PS:感谢@mz,他通过stackoverflow =给我一些帮助)
更新
我几乎用这种方法:
隐式对象validityFormat extends格式[有效性] {
覆写高清写入(o:有效性):JsValue = {
Json.obj(
id - > JsString(o.id.getOrElse(null).toString),
objectType - > JsString(o.objectType),
因为 - > JsString(o.since.toString),
until - > JsString(o.since.toString),
objectId - > JsString(o.objectId.toString ),
validityTargetId - > JsString(o.validityTargetId.toString),
validityTargetType - > JsString(o.validityTargetType))
}
overrid (json:JsValue):JsResult [Validity] = {
JsSuccess(有效性(
(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。
UPDATE
我做了一个可行的例子,但我接受了@ mz答案,吃了比我的,但都工作得很好。最大的区别是,在我的方法中,我需要为DateTime和UUID提供一些自定义转换器,而@mz方法不适用于您!
隐式对象UUIDFormatter extends Format [UUID] {
override def reads(json:JsValue):JsResult [UUID] = {
val uuid = json.validate [String]
JsSuccess (uUID.fromString(uuid.get))
}
覆写def write(o:UUID):JsValue = {
JsString(o.toString)
}
}
隐式对象DateTimeFormatter extends Format [DateTime] {
覆盖def reads(json:JsValue):JsResult [DateTime] = {
datetime = json。验证[Long]
JsSuccess(new DateTime(datetime.get))
}
覆盖def write(o:DateTime):JsValue = {
JsNumber(o .getMillis)
}
}
隐式对象validityFormat extends格式[有效性] {
覆盖高清写入(o:有效性):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))
}
覆盖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])
)
}
这里有几个问题,但它们都与泛型没有关系,因为您正在处理具体类型<$ c $首先,使用 Reads
组合子的最后一个参数应该是。
unlift
和写入
。 第二,组合器中的类型必须映射到
implicit val reads:Reads [Validity] =(
(JsPath \id)。readNullable [UUID] and // readNullable reads to Option
(JsPath \objectType)。阅读[String]和
(JsPath \since)。read [DateTime] and
(JsPath \until)。read [DateTime] and
(JsPath \读取[UUID]和
(JsPath \validityTargetId)。read [UUID]和
(JsPath \validityTargetType)。read [String]
)( Validity.apply _)
读取
已存在对于 UUID
和 DateTime
,所以这应该可以。
<同样,
写入[有效性]
看起来像这样: 隐式val写入:写入[有效性] =(
(JsPath \id)。writeNu (JsPath \objectType)。write [String]和
(JsPath \since)。write [DateTime]和
(JsPath \写入[UUID]和
(JsPath \validityTargetId)。write [UUID]和
(写入) JsPath \validityTargetType)。write [String]
)(unlift(Validity.unapply))
I have a Crud Controller in my application which works perfectly with JsonInception, but fail with a custom json converter.
Follow the given case class:
case class Validity(id: Option[UUID], objectType: String, since: DateTime, until: DateTime, objectId: UUID, validityTargetId: UUID, validityTargetType: String)
I have an object companion as follow:
object Validity extends CrudObject[Validity] {
implicit val reads = Json.reads[Validity]
implicit val writes = Json.writes[Validity]
}
Where my CrudObject is a trait with the given code:
trait CrudObject[T] {
val reads: Reads[T]
val writes: Writes[T]
}
This is needed since I'm working in a Generic Crud. Without this, Play is unable to find any implicit converter.
So my Generic Crud Controller, is something like this:
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
and for each controller, I have the follow:
object ValidityController extends CrudController[Validity] {
override def service: GenericServiceModule[Validity] = ServiceModule
override def companion: CrudObject[Validity] = Validity
}
Ok, with this design in mind, as I said, works perfectly, I need to design a custom json converter. Yes, I know, it's pretty simple, but something is happening and I don't know how to get rid of that.
Now I'm trying to do the following:
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 _))
and it gives me:
Type mismatch, expected: (NotInferedA) => Option[NotInferedB], actual: (CrudObject[Nothing]) => CrudObject[Nothing]
I believe this is happen cuz CrudObject is a trait and does not have apply and unapply.
Anyway, removing CrudObject gives me a similar error:
Type mismatch, expected: (NotInferedA) => Option[NotInferedB], actual: (Option[UUID], String, DateTime, DateTime, UUID, UUID, String) => Validity
but even if I can solve this, I can't imagine living without CrudObject due my GenericCrud.
Any Thoughts?
PS: Thanks to @m-z who has been giving me some assist through stackoverflow =)
UPDATE
I'm almost there with this approach:
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])
)
}
}
Using this way, which is different from documentation Scala Combinators, I don't get the previous error, which is good, no type mismatch =)
Now I'm working out on to figure out how to convert to UUID and DateTime.
UPDATE
I did an example that works, but I accepted the @m-z answer because there are less boilerplate than mine, but both worked fine. The big difference is, in my approach I needed to provide some custom converters for DateTime and for UUID, whereas @m-z approach you don't!
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])
)
}
There are a couple problems here, but neither of them are relevant to generics, because you're dealing with the concrete type Validity
.
First, the last argument using Reads
combinators should be (Validity.apply _)
. You would only use unlift
with Writes
.
Second, the types in the combinators must map to the types in your 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
already exist for UUID
and DateTime
, so this should work okay.
Similarly, Writes[Validity]
would look like this:
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自定义Json的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!