How to ensure strict JSON serialization in Play 2.x

Playback JSON serialization is by default resolvable when serialized from JSON to case class. For example.

case class Stuff(name: String, value: Option[Boolean])

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

If the following JSON was received:

{name: "My Stuff", value: true, extraField: "this shouldn't be here"}

This will succeed using "JsSuccess" and reset "extraField".

Is there a way to build a Json Reads function to return a JsError if there are "raw" fields?

+3
source share
2 answers

You can make sure that before performing your own decoding, the object does not contain additional keys:

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

Or, if you don't like the error messages (and this is not very useful, anyway):

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

And then:

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

Repetition is not very nice, but it works.

+5
source share

Inspired from Travis ' comment for using LabelledGeneric I was able to achieve safe compile time.

 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 

now keys.map (toStringName).toList will give you

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

+3
source share

All Articles