如何在Play 2.x中强制执行JSON的严格序列化

5
默认情况下,Play的JSON序列化在将JSON序列化为案例类时是宽容的。例如:
case class Stuff(name: String, value: Option[Boolean])

implicit val stuffReads: Reads[Stuff] = (
  ( __ \ 'name).read[String] and
  ( __ \ 'value).readNullable[Boolean]
)(Stuff.apply _)

如果收到以下JSON:
{name: "My Stuff", value: true, extraField: "this shouldn't be here"}

如果成功,它将使用“JsSuccess”并且舍弃“extraField”。

是否有一种方法构建Json Reads函数,使其在存在“未处理”的字段时返回JsError?

2个回答

5

在执行自己的解码之前,您可以验证对象不包含额外的键:

import play.api.data.validation.ValidationError

def onlyFields(allowed: String*): Reads[JsObject] = Reads.filter(
  ValidationError("One or more extra fields!")
)(_.keys.forall(allowed.contains))

如果您不关心错误消息(而且那个也不是很有帮助):

def onlyFields(allowed: String*): Reads[JsObject] =
  Reads.verifying(_.keys.forall(allowed.contains))

然后:
implicit val stuffReads: Reads[Stuff] = onlyFields("name", "value") andThen (
  (__ \ 'name).read[String] and
  (__ \ 'value).readNullable[Boolean]
)(Stuff)

重复不太好,但它有效。

想知道您是否会采用不同的方法来增加编译时魔法,以获取case class属性名称而不是onlyFields("name", "value")可变参数函数?在onlyFields方法中硬编码属性名称可以工作,但在修改case class时遇到维护挑战,需要记住更新/添加/删除onlyFields中的字段名称,因为onlyFields没有编译时安全性,它只有在单元测试/运行时才能显示缺少的属性。是否有一种方法可以在编译时实现这一点? - Vikas Pandya
你可以使用宏来完成这个任务,或者更加简洁地使用Shapeless的LabelledGeneric。我会尽力写一篇关于LabelledGeneric的博客文章,但是我不能保证本周内能完成。 - Travis Brown

3

Travis 评论的启发,我能够使用 LabelledGeneric 实现编译时安全的解决方案。

object toStringName extends Poly1 {
    implicit def keyToStrName[A] = at[Symbol with A](_.name)
}
case class Foo(bar: String, boo: Boolean)

val labl = LabelledGeneric[Foo]
val keys = Keys[labl.Repr].apply

现在 keys.map (toStringName).toList 将会给你

res0: List[String] = List(bar, boo)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接