2014-04-25 26 views
3

從JSON序列化到案例類時,Play的JSON序列化默認爲寬容。例如。如何在Play 2.x中強制執行嚴格的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讀取函數,讓它返回一個JsError如果有'未處理'的字段?

回答

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) 

重複是不是很漂亮,但它活像KS。

+0

想,如果你今天會做到這一點不同,以增加更多的編譯時間魔法讓'情況class'屬性名稱VS'onlyFields(「名」, 「值」)'可變參數函數? ''onlyFields'方法中的硬編碼屬性名稱工作正常,但遇到維護挑戰時需要記住在'case class'被修改時在'onlyFields'中更新/添加/刪除字段名稱,因爲'onlyFields'沒有編譯時安全性,屬性直到單元測試/運行時。有什麼方法可以在編譯時實現這一點? –

+0

你完全可以用宏來做到這一點,或者更加乾淨利用Shapeless的'LabelledGeneric'。我會盡量找一些時間寫一篇博客文章來做這個'LabelledGeneric',但我不能保證我會在這周得到它。 –

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)

相關問題