def fromArrayIds(field: String) = (__ \ field).json.update ( of[JsArray].map { case JsArray(list) => JsArray(list.map(_ \ "$oid")) } )
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import play.modules.reactivemongo.json.BSONFormats._
/**
* Transforms a JsArray using the provided Reads; cumulating errors.
*
* @param reads the transforming Reads
* @tparam A the Type deserialized by the given Reads
* @return a JsArray transforming Reads
*/
def tfList[A <: JsValue](reads: Reads[A])
: Reads[JsArray] = Reads {
case arr: JsArray =>
val init: JsResult[Seq[JsValue]] = JsSuccess(Seq[JsValue]())
arr.value.foldLeft(init) { (acc, e) =>
acc.flatMap(seq => e.transform(reads).map(seq :+ _))
} map JsArray
case _ => JsError("expected JsArray")
}
(__ \ "str_list").json.pickBranch(tfList(medStr))
val medStr: Reads[JsString] = of[JsString] keepAnd minLength[String](1) keepAnd maxLength[String](512)
I have this json:And I want to add an inner field into each field_name by concatenating a string and filed_id, so the output would be something like this:
{
"activities": [
{
"id": "1",
"project": {
"p_id": "p_3"
}
},
{
"id": "2",
"project": {
"p_id": "p_4"
}
}
]
}
{
"activities": [
{
"id": "1",
"project": {
"p_id": "p_3",
"field": "new_p_3"
}
},
{
"id": "2",
"project": {
"p_id": "p_4",
"field": "new_p_4"
}
}
]
}
Please can you tell me How can I use your function to do this ?
case class Activity(id: String, project: Project)
object Activity {
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax.
_
import MY.common.Validators._
// For your "original" format, which I assumed was for an API to talk to a database.
implicit val mongoFmt = Json.format[Activity]
// Common functionality for all transformers
//idStr is just the validator/Reads for your ID Strings.
private val coreReads = (__ \ "id").json.pickBranch(idStr)
val req2mongo = (
(__ \ "project").json.pickBranch(Project.req2mongo) and
coreReads
) reduce
val mongo2resp = (
(__ \ "project").json.pickBranch(Project.mongo2resp) and
coreReads
) reduce
}
case class Project(p_id: String)
object Project {
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax.
_
import MY.common.Validators._ //where "idStr" is defined
// For your "original" format, which I assumed was for an API to talk to a database.
implicit val mongoFmt = Json.format[Activity]
// Common functionality for all transformers
private val coreReads = (__ \ "p_id").json.pickBranch(idStr)
// Generates the value in field based on the data that was placed there in mongo2resp.
private val genField = (__ \ "field").json.update (
of[JsString].map { jsStr =>
JsString(
"new_"+
jsStr.value.trim)
}
)
val req2mongo = coreReads
val mongo2resp = (
(__ \ "field").json.copyFrom((__ \ "id").json.pick) and
coreReads reduce
) andThen genField
}
// Here's where we actually use tfList.
private val activityListTransform = (__ \ "activities").json.pickBranch(tfList(Activity.mongo2resp))
protected def outputJson(input: JsObject): JsResult[JsObject] = {
input.transform(activityListTransform)
}
case class Project(
p_id: String,
name: String,
description: String,
users: Seq[ProjectUser]
)
object Project {
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax.
_
import MY.common.Validators._ //where "idStr", et al. are defined
// For your "original" format, which I assumed was for an API to talk to a database.
implicit val mongoFmt = Json.format[Activity]
// Common functionality for all transformers
private val coreReads = (
(__ \ "p_id" ).json.pickBranch(idStr) and
(__ \ "name" ).json.pickBranch(smallStr) and
(__ \ "description").json.pickBranch(longStr)
) reduce
// Generates the value in field based on the data that was placed there in mongo2resp.
private val genField = (__ \ "field").json.update (
of[JsString].map { jsStr =>
JsString("new_"+jsStr.value.trim)
}
)
// The generated "field" isn't here, because I assume that you
// don't want to save something like that to the database.
val req2mongo = (
(__ \ "users").json.pickBranch(tfList(ProjectUser.req2mongo)) and
coreReads
) reduce
val mongo2resp = (
(__ \ "users").json.pickBranch(tfList(ProjectUser.mongo2resp)) and
(__ \ "field").json.copyFrom((__ \ "id").json.pick) and
coreReads reduce
) andThen genField
}
If anything else needs to be transformed between Database and REST API, put them in req2mongo and mongo2resp. If no transformation is required, put it in coreReads....