Blankman
Blankman

Reputation: 267040

bind a JSON value to a Scala class using play

I have a fairly complicated form that lets the user select some drop down values and input text in input boxes etc.

The form is essentially a 'Advanced Search' type form.

def search = Action(parse.json) { request =>
    val sR = request.body.validate[SearchRequest]
    sR.fold(
      errors => {
        BadRequest(Json.obj("status" -> "KO", "message" -> JsError.toJson(errors)))
      },
      searchRequest => {
        Logger.debug(s"searchRequest is $searchRequest")
        val sr = SearchResults(10)
        Ok(Json.toJson(sr))
      }
    )
  }

Say I have a drop down list that has the following values:

IsGreaThan
IsLessThan
EqualTo

My SearchRequest looks like:

case class SearchRequest(operator: String)

But I don't want the user to potentially send bad data i.e. a string other than 'IsGreaterThan', 'IsLessThan' etc.

How can I make it strongly typed when the JSON binds to the SearchRequest object?

Upvotes: 1

Views: 374

Answers (1)

millhouse
millhouse

Reputation: 10007

I think what you're looking for is a Reads instance for an enumerated type; i.e.:

Declare a specific SearchOperator enum:

object SearchOperator extends Enumeration {
  type SearchOperator = Value

  val IsGreaThan = Value("IsGreaThan")
  val IsLessThan = Value("IsLessThan")
  val EqualTo = Value("EqualTo")
}

Use it in the SearchRequest case class:

case class SearchRequest(lhs: Int, operator: SearchOperator, rhs:Int)

Define how to read it:

object SearchRequestJson {
  implicit val soReads = Reads.enumNameReads(SearchOperator)
  implicit val srReads = Json.reads[SearchRequest]
}

The key thing being the Reads.enumNameReads function which was added to Play in version 2.4, and saves a heap of boilerplate for your situation.

Now, if I submit an illegal value as part of the search request:

{ "lhs": 42, "operator": "NotEqualTo", "rhs": 44 }

I get a:

JsError(
  List(
    (/operator, List(
      ValidationError(
        List(error.expected.enumstring),
        WrappedArray()
      )
    ))
  )
)

Upvotes: 2

Related Questions