【问题标题】:Serialize set to json with a custom Writes in Scala Play 2.4使用 Scala Play 2.4 中的自定义写入将序列化设置为 json
【发布时间】:2016-10-24 02:54:21
【问题描述】:

我有一个用例,我想将用户实体序列化为 json。此用户实体包括我不想公开的私有字段,例如密码。

当我返回单个用户时,我正在使用自定义 OWrites 来处理这个问题:

val userSafeWrites: OWrites[User] = (
      (__ \ EMAIL_FIELD).write[String] ~
      (__ \ FIRST_NAME_FIELD).write[String] ~
      (__ \ LAST_NAME_FIELD).write[String] ~
      (__ \ PHONE_NUMBER_FIELD).write[Option[String]] ~
      (__ \ ID_FIELD).write[Long]
    )(p => (p.email, p.firstName, p.lastName, p.phoneNumber, p._id.get))

然后我指定 OWrites 而不是使用隐式:

 Ok(Json.toJson(user)(User.userSafeWrites))

但是,我现在必须返回 Set[User]

我该怎么做?我需要实现OWrites[Set[User]] 吗?如果我要返回一个带有字段名称的对象以及结果,我可以理解如何做到这一点,例如:

{
  "users": [{user1}, {user2}]
}

但是,我只想返回一个数组,以适应其他端点的输出:

[{user1}, {user2}]

或者我应该将集合的每个元素映射到 JsObject 并将自定义 OWrites 应用于每个对象?最有效的方法是什么?

我觉得这很简单,我只是因为自己没有找到答案而成为白痴。

【问题讨论】:

  • 您必须在隐式范围内“公开”您的OWrites[User](显式导入它,或者将它放在User 的伴随对象中)。然后,Play 提供的Writes[Set[T]] 将与T = User 一起工作,无需执行更多操作。
  • 在这种情况下,我不能将 OWrites 作为隐式,因为在隐式范围内我已经有另一个 OWrites[User]。我对这个类有两个不同的序列化要求。
  • 没有什么能阻止您将 this 作为本地隐式导入(仅在一个函数的范围内,而不是整个类)。

标签: json scala playframework playframework-2.4


【解决方案1】:

您正在重新实现可遍历的写入,这些写入无论如何都是微不足道的,但仍然如此。另一种选择是重用 Writes

val users: Set[User] = ???
Json.toJson(users)(Writes.traversableWrites(userSafeWrites))

WritesTraversable 的定义如下:

implicit def traversableWrites[A: Writes] = Writes[Traversable[A]] { as =>
  JsArray(as.map(toJson(_)).toSeq)
}

这正是你所做的。

它接受Writes[A] 类型的隐式参数并使用它来编写每个成员。您可以显式传递此参数以获得所需的Writes

@cchantep 提到的另一个选项就是在需要的地方导入你的隐式。

例如,有一些具有默认写入的模型

case class User(num: Int)

object User {
  implicit val writes = Json.writes[User]
}

和另一个具有自定义写入的对象

object OtherWrites {
  implicit val custom: Writes[User] = new Writes[User] {
    override def writes(o: User): JsValue = JsNull
  }
}

在作为客户端的类中

object Client extends App {
  val obj = List(User(1))
  print(Json.toJson(obj))
}

您会得到 [{"num":1}] 打印,因为默认写入存在于隐式范围中,因为它们是在 User 的伴随对象中定义的。

您可以在需要的地方导入自定义写入,并且来自伴随对象的写入将被覆盖

object Client extends App {
  import test.OtherWrites._
  val obj = List(User(1))
  print(Json.toJson(obj))
}

你会得到[null] 打印出来。

【讨论】:

  • 是的 Json.toJson(users)(Writes.traversableWrites(userSafeWrites)) 成功了。比我的更清洁的解决方案。 Tks
  • 请注意,如果您有例如Map[String, List[Set[Option[User]]]] 传递 Writes[User] 会很痛苦,这个简单的导入可以让你免于困境。
  • 找不到Writes要导入哪个包???我的日食很糟糕,自动导入失败,我在谷歌中找不到它,因为Writes 太常见了!!我已经导入play.api.libs.json.Writes._!!!
  • @Zennichimaro 您在Writes 的伴随对象中导入了东西。您应该导入 play.api.libs.json.Writes 以在范围内获取此符号。但您可能更容易导入play.api.libs.json._ 以避免进一步的问题。您可以在 Play 的文档 here 中找到导入和示例
【解决方案2】:

嗯,很简单:

Ok(JsArray(userSet.map(user => Json.toJson(user)(User.userSafeWrites)).toSeq))

在发布问题之前,花了一个多小时在谷歌上搜索并尝试以最荒谬的方式对其进行整理。 发布问题后一分钟内解决。

有一个这样的日子...... 除非有更好的方法来做到这一点

【讨论】:

    猜你喜欢
    • 2014-11-17
    • 2015-12-12
    • 1970-01-01
    • 2015-04-21
    • 1970-01-01
    • 2016-08-03
    • 1970-01-01
    • 1970-01-01
    • 2020-09-27
    相关资源
    最近更新 更多