j3d
j3d

Reputation: 9734

Play: How to implement a conditional JSON validator

My Play application has a JSON validator like this:

val validateAccount = (
  ((__ \ 'id).json.pickBranch) ~
  ((__ \ 'name).json.pickBranch) ~ // mandatory
  ((__ \ 'mobile).json.pickBranch or emptyObj) ~ // optional
  ...
).reduce

The validator above accepts JSON that contains at least an id and optionally a name. Is it possible to make a field mandatory depending on a condition? For instance, how do I make name mandatory when a condition is true and keep it optional when that condition is false?

def validateAccount(condition: Boolean) = (
  ((__ \ 'id).json.pickBranch) ~
  ((__ \ 'name).json.pickBranch if (condition) or emptyObj else ???) ~
  ((__ \ 'mobile).json.pickBranch or emptyObj) ~
  ...
).reduce

I want emptyObj if and only if codition is trueemptyObj just represents an empty node:

val emptyObj = __.json.put(Json.obj())

A [bad] solution could be something like this:

def validateAccount(condition: Boolean) = {
  (if (condition) {
    ((__ \ 'id).json.pickBranch) ~
    ((__ \ 'name).json.pickBranch) ~ // mandatory
    ((__ \ 'mobile).json.pickBranch or emptyObj) ~ // optional
    ...
  } else {
    ((__ \ 'id).json.pickBranch) ~
    ((__ \ 'name).json.pickBranch or emptyObj) ~ // now this is optional
    ((__ \ 'mobile).json.pickBranch or emptyObj) ~ // optional
    ...
  }).reduce
}

The reason I need conditional JSON validation is that when my REST api gets an insert request, the JSON has to be complete, while when it gets an update request, only the field to be updated should be provided.

Upvotes: 3

Views: 1091

Answers (1)

j3d
j3d

Reputation: 9734

OK... here is the solution:

package object typeExtensions {

  import play.api.libs.json._
  import play.api.libs.functional.syntax._

  implicit class ReadsExtensions(reads: Reads[JsObject]) extends AnyVal {

    def orEmpty = reads | __.put(Json.obj())
    def orEmptyIf(b: Boolean) = if (b) orEmpty else reads 
  }
}

object MyObject {

  def validateAccount(update: Boolean) = (
    ((__ \ 'id).json.pickBranch orEmptyIf update) ~ // can be empty only if update is true
    ((__ \ 'name).json.pickBranch orEmtpy) ~        // can be always empty
    ((__ \ 'mobile).json.pickBranch orEmpty) ~      // can be always empty
    ...
  ).reduce

  ...
}

I hope that helps ;-)

Upvotes: 4

Related Questions