Reputation: 1723
I'm using Playframework 2.2.1 with Scala, and I'm writing a REST API where users can set partially unstructured data. Basically, users will POST JSON that looks something like this:
{
"id": 1,
"name": "MyObject",
"properties": [
"myFirstProperty": "Value 1",
"mySecondProperty": "Value 2"
]
}
and I want it to bind to a case class that looks like:
case class Preference(id: Long, name: String, properties: Map[String, String])
I am hoping to take advantage of Play's forms API for that so I can have a bunch of nice validation options for free, but I haven't been able to figure out a straightforward way to do that. Ideally (at least in my mind), I would be able to define a Form object along the lines of:
Form(
mapping(
"id" -> longNumber,
"name" -> nonEmptyText(min = 5),
"properties" -> map(nonEmptyText, nonEmptyText)
)(Preference.apply)(Preference.unapply)
)
Unfortunately, the "map(text, text)" is fictional. Has anyone done a similar binding to this using the forms API? If so, how? If not, I'm curious what you used instead for validation. Was it all done by hand?
Thanks in advance for the help!
Upvotes: 4
Views: 1306
Reputation: 21017
Maybe I'm misunderstanding your question, but doesn't the reads
macro do what you want?
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val preferenceReads = Json.reads[Preference]
With that implicit in scope, you can use as
, asOpt
, or validate
to convert your JSON value to a Preference
(see the Play ScalaJson docs).
Upvotes: 2
Reputation: 1317
As far as I know the forms API and the json support are not meant to be mixed together. But that shouldn't be a problem since you can accomplish pretty much every validation with either forms or json reads.
import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.libs.json.Reads._
import play.api.data.validation.ValidationError
val nonEmptyKeyValueMapReads =
filter[Map[String, String]](ValidationError("error.invalid"))(properties =>
properties.keys.find(_.size == 0).isEmpty && properties.values.find(_.size == 0).isEmpty)
val preferencesReads =
((__ \ 'id).read[Long] and
(__ \ 'name).read[String](minLength[String](5)) and
(__ \ 'properties).read[Map[String, String]](nonEmptyKeyValueMapReads))(Preferences)
There are several ways to achieve your properties, this is one of them. After defining the reads you can use it to validate your data, e.g. myJson.validate(preferencesReads)
Upvotes: 1