I need advice on Play Json and elegant handling of parameters in Writes dash

I have a question like "style" or "effective scala" here: I have a "FeatureCheck" class that I need to convert to a Json in Play structure.

case class FeatureCheck(val result: Option[Boolean], val missing: Option[Array[String]], val error: Option[String]) 

I serialize it using my own "records", but my code is very similar to Java code. I would like to serialize each parameter in the validation object only if it is defined (the final object should not have empty values).

 def writes(check: FeatureCheck): JsValue = { val builder = Seq.newBuilder[(String, JsValue)] if (check.error.isDefined) { builder += "error" -> JsString(check.error.get) } if (check.missing.isDefined) { builder += "missing" -> Json.toJson(check.missing.get) } if (check.result.isDefined) { builder += "result" -> JsBoolean(check.result.get) } JsObject(builder.result) } 

So I was wondering if there is a way to do this without these ugly if-then or even to remove the builder for the sequence.

Thanks for any help or comment.

Clarrifications:

Let's say I just want to send result = true. I want the resulting Json to be:

 {"result":true} 

and NOT

 { "result": true, "error": null, "missing": [] } 
+7
source share
3 answers

Given that you can simply add the parameter to seq (see Add to the list if the value is not null), you can do what you want quite elegantly:

 type JsField = (String, JsValue) def writes(check: FeatureCheck): JsValue = { JsObject( Seq[JsField]() ++ check.error.map("error" -> JsString(_)) ++ check.missing.map("missing" -> Json.toJson(_)) ++ check.result.map("result" -> JsBoolean(_)) ) } 
+4
source

You can skip the additional check isDefined , it will check only the value of the Some parameter. getOrElse will do the same for Option in scala.

This is a slightly simpler version.

 def writes(check: FeatureCheck): JsValue = { val builder = Seq.newBuilder[(String, JsValue)] builder += "error" -> JsString(check.error.getOrElse("")) builder += "missing" -> Json.toJson(check.missing.getOrElse({ Array[String]() })) builder += "result" -> JsBoolean(check.result.getOrElse(false)) JsObject(builder.result) } 

And this can be considered even simpler.

 def writes(check: FeatureCheck): JsValue = JsObject( List( "error" -> JsString(check.error.getOrElse("")), "missing" -> Json.toJson(check.missing.getOrElse({ Array[String]() })), "result" -> JsBoolean(check.result.getOrElse(false)) ) ) 

EDIT

If you do not want the properties to be added, if they are missing, this is more elegant, I think:

 def writes(check: FeatureCheck): JsValue = JsObject( List( check.error.map("error" -> JsString(_)), check.missing.map("missing" -> Json.toJson(_)), check.result.map("result" -> JsBoolean(_)) ).flatten ) 
+4
source

Another option you have is to filter out the null fields after creating the Json object, I use the following usage method:

  def filterOutNullFields(jsonObject:JsObject) : JsObject = { jsonObject.fields.foldLeft(jsonObject)((jsonObjectAcc , tupleKeyValue) => { if (tupleKeyValue._2.isInstanceOf[JsObject]) { val innerJsonObject = filterOutNullFields(tupleKeyValue._2.as[JsObject]) jsonObjectAcc - tupleKeyValue._1 + (tupleKeyValue._1, innerJsonObject) } else { if (tupleKeyValue._2.equals(JsNull)) { jsonObjectAcc - tupleKeyValue._1 } else { jsonObjectAcc } } }) } 

You can just use it like:

 val featureCheckJson = Json.toJson(featureCheck) val featureCheckJsonWithoutNullFields = filterOutNullFields(featureCheckJson.as[JsObject]) 
0
source

All Articles