Validation Inception

Introduction

The validation API provides macro-based helpers to generate Rule and Write for case classes (or any class with a companion object providing apply / and unapply methods).

The generated code:

  • is completely typesafe
  • is compiled
  • does not rely on runtime introspection at all
  • is strictly equivalent to a hand-written definition

Example

Traditionally, for a given case class Person we would define a Rule like this:

scala> case class Person(name: String, age: Int, lovesChocolate: Boolean) defined class Person 
import jto.validation._ import play.api.libs.json._ implicit val personRule: Rule[JsValue, Person] = From[JsValue] { __ => import jto.validation.playjson.Rules._ ((__ \ "name").read[String] ~ (__ \ "age").read[Int] ~ (__ \ "lovesChocolate").read[Boolean])(Person.apply) } 

Let's test it:

scala> val json = Json.parse("""{ | "name": "Julien", | "age": 28, | "lovesChocolate": true | }""") json: play.api.libs.json.JsValue = {"name":"Julien","age":28,"lovesChocolate":true} scala> personRule.validate(json) res1: jto.validation.VA[Person] = Valid(Person(Julien,28,true)) 

The exact same Rule can be generated using Rule.gen:

import jto.validation._ import play.api.libs.json._ implicit val personRule = { import jto.validation.playjson.Rules._ // let's not leak implicits everywhere Rule.gen[JsValue, Person] } 

The validation result is identical :

scala> val json = Json.parse("""{ | "name": "Julien", | "age": 28, | "lovesChocolate": true | }""") json: play.api.libs.json.JsValue = {"name":"Julien","age":28,"lovesChocolate":true} scala> personRule.validate(json) res3: jto.validation.VA[Person] = Valid(Person(Julien,28,true)) 

Similarly we can generate a Write:

import jto.validation._ import play.api.libs.json._ implicit val personWrite = { import jto.validation.playjson.Writes._ // let's no leak implicits everywhere Write.gen[Person, JsObject] } 
scala> personWrite.writes(Person("Julien", 28, true)) res5: play.api.libs.json.JsObject = {"name":"Julien","age":28,"lovesChocolate":true} 

Known limitations

  • Don’t override the apply method of the companion object. The macro inspects the apply method to generate Rule/Write. Overloading the apply method creates an ambiguity the compiler will complain about.
  • Macros only work when apply and unapply have corresponding input/output types. This is naturally true for case classes. However if you want to validate a trait, you must implement the same apply/unapply you would have in a case class.
  • Validation Macros accept Option/Seq/List/Set & Map[String, _]. For other generic types, you'll have to test and possibly write your Rule/Write if it's not working out of the box.

results matching ""

    No results matching ""