繁体   English   中英

在Playframework 2.1中修改JSON读写

[英]Modifying JSON reads and writes in playframework 2.1

我是新手和scala /玩家,需要playframework的JSON读/写方面的帮助。

我使用Json.reads [T]和Json.writes [T]宏来定义我的类的json读写。 但是我想有一个(总是)映射不同的属性名称。 即,我在类中有一个名为id属性,并且我希望在将对象转换为json时将其表示为_id ,反之亦然。

有没有一种方法可以修改由Json.reads和Json.writes宏生成的读取/写入对象来实现此目的,还是我必须手动重写读取和写入才能使一个属性具有不同的名称?

编辑

让我尝试更好地解释这个问题。 考虑模型对象User:

case class User (id: BigInt, email: String, name: String)

为了在REST API的上下文中提供json的目的而将User序列化为json时,json应该如下所示:

{“ id”:23432,“ name”:“ Joe”,“电子邮件:” joe@example.com“}

为存储/更新/读取表单而将User序列化为json时,MongoDB json应该看起来像:

{“ _id”:23432,“名称”:“ Joe”,“电子邮件:” joe@example.com“}

换句话说一切是除了当与蒙哥通信的相同id应该被表示为_id

我知道我可以按照答案中达西·邱的建议为每个模型对象手动编写两组读写(一组用于Web,另一组用于与Mongo进行通信),但是保持两组读写几乎相同。相同的,除了id属性似乎重复了很多代码,所以我想知道是否有更好的方法。

首先,您要定义来回重命名id / _id的转换:

import play.api.libs.json._
import play.modules.reactivemongo.json._

val into: Reads[JsObject] = __.json.update( // copies the full JSON
  (__ \ 'id).json.copyFrom( (__ \ '_id).json.pick ) // adds id
) andThen (__ \ '_id).json.prune  // and after removes _id

val from: Reads[JsObject] = __.json.update( // copies the full JSON
  (__ \ '_id).json.copyFrom( (__ \ 'id).json.pick ) // adds _id
) andThen (__ \ 'id).json.prune  // and after removes id

(要了解为什么Reads是一种转换,请阅读: https : //www.playframework.com/documentation/2.4.x/ScalaJsonTransformers

假设我们有为实体类生成的宏WritesReads

def entityReads: Reads[T] // eg Json.reads[Person]
def entityWrites: Writes[T] // eg Json.writes[Person]

然后,我们将转换与宏生成的代码混合:

private[this] def readsWithMongoId: Reads[T] = 
  into.andThen(entityReads)
private[this] def writesWithMongoId: Writes[T] =
  entityWrites.transform(jsValue => jsValue.transform(from).get)

最后一件事情。 Mongo驱动程序希望确保(即,类型安全性确保)它插入的json是JsObject。 这就是为什么我们需要OWrites 我没有找到比以下更好的方法:

private[this] def oWritesWithMongoId = new OWrites[T] {
  override def writes(o: T): JsObject = writesWithMongoId.writes(o) match {
    case obj: JsObject => obj
    case notObj: JsValue => 
      throw new InternalError("MongoRepo has to be" +
      "definded for entities which serialize to JsObject")
  }

}

最后一步是将隐式OFormat

implicit val routeFormat: OFormat[T] = OFormat(
  readsWithMongoId,
  oWritesWithMongoId
)

假设您的案例类(在您的问题中为T被命名为User并具有如下定义

case class User(_id: String, age: Int)

您的阅读可以定义为

implicit val userReads = new Reads[User] {
  def reads(js: JsValue): User = {
    User(
      (js \ "id").as[String],
      (js \ "age").as[Int]
    )
  }
}

您的writes[User]应该遵循相同的逻辑。

如果您输入了足够的代码,则可以使用转换器实现此目的:

val idToUnderscore = (JsPath).json.update((JsPath).read[JsObject].map { o:JsObject =>
  o ++ o.transform((JsPath\"_id").json.put((JsPath\"id").asSingleJson(o))).get
}) andThen (JsPath\"id").json.prune
val underscoreWrite = normalWrites.transform( jsVal => jsVal.transform(idToUnderscore).get )

这是完整的测试:

import play.api.libs.functional.syntax._
import play.api.libs.json._

val u = User("overlord", "Hansi Meier", "evil.overlord@hansi-meier.de")

val userToProperties = {u:User => (u.id, u.name, u.email)}
val normalWrites = (
   (JsPath\"id").write[String] and
   (JsPath\"name").write[String] and
   (JsPath\"email").write[String]
 )(userToProperties)

val idToUnderscore = (JsPath).json.update((JsPath).read[JsObject].map { o:JsObject =>
  o ++ o.transform((JsPath\"_id").json.put((JsPath\"id").asSingleJson(o))).get
}) andThen (JsPath\"id").json.prune
val underscoreWrite = normalWrites.transform( jsVal => jsVal.transform(idToUnderscore).get )

info(Json.stringify(Json.toJson(u)(normalWrites)))
info(Json.stringify(Json.toJson(u)(underscoreWrite)))

现在,如果您修改normalWrites(例如,通过添加其他属性),则underscoreWrite仍会执行您想要的操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM