I have the following json string:
{
"id":123,
"students":[
{
"collected":{
"field":"field_1"
},
"attr":[{
"name":"test_name",
"age":"17",
"color":"blue"
}]
}
]
}
I want to update all the elements in "attr" object in all of my "students" list. By updating, I mean that I need to update the values into the concatenated string of the key and value
{
"id":123,
"students":[
{
"collected":{
"field":"field_1"
},
"attr":[{
"name":"nametest_name",
"age":"age17",
"color":"colorblue"
}]
}
]
}
I have come across into transform method of JsValue. This is my transformer:
val jsonTransformer = (__ \ 'students).json.update(
of[JsArray].map{
case JsArray(list) =>
list.map(o => o)
}
)
My transformer is not changing anything because I can't seem to find a way to traverse to the "attr" field.
Can anyone provide me an insight?
Taking following as input (where attr
is an array of objects):
val input =
"""
|{
| "id":123,
| "students":[
| {
| "collected":{
| "field":"field_1"
| },
| "attr":[{
| "name":"test_name",
| "age":"17",
| "color":"blue"
| }]
| },
| {
| "collected":{
| "field":"field_2"
| },
| "attr":[{
| "name":"test_name2",
| "age":"18",
| "color":"red"
| }]
| }
| ]
|}
|""".stripMargin
We can apply following transformer to get the required results:
val attrTransformer = (__ \ "attr").json.update {
__.read[JsArray].map {
case JsArray(values) =>
val updatedValues = values.map { x =>
JsObject(x.as[JsObject].fields.map { z =>
val (key, value) = z
(key, JsString(key + value.as[String]))
})
}
JsArray(updatedValues)
}
}
val transformer = (__ \ "students").json.update(Reads.list(attrTransformer).map(x => JsArray(x)))
val output = json.transform(transformer).get
The output after transformation will be:
{
"students" : [ {
"collected" : {
"field" : "field_1"
},
"attr" : [ {
"name" : "nametest_name",
"age" : "age17",
"color" : "colorblue"
} ]
}, {
"collected" : {
"field" : "field_2"
},
"attr" : [ {
"name" : "nametest_name2",
"age" : "age18",
"color" : "colorred"
} ]
} ],
"id" : 123
}
Considering the below input (where attr
is not an array of JsObject
):
val input =
"""
|{
| "id":123,
| "students":[
| {
| "collected":{
| "field":"field_1"
| },
| "attr":{
| "name":"test_name",
| "age":"17",
| "color":"blue"
| }
| },
| {
| "collected":{
| "field":"field_2"
| },
| "attr":{
| "name":"test_name2",
| "age":"18",
| "color":"red"
| }
| }
| ]
|}
|""".stripMargin
A simple solution would be to create a new JsObject with updated values as below. (without any validations)
val students = (Json.parse(input) \ "students").as[JsArray]
val requiredStudents = students.value.map { student =>
val attr = student \ "attr"
val updatedAttributes = attr.get.as[JsObject].fields.map { x =>
val (key, value) = x
(key, JsString(key + value.as[String]))
}
val requiredStudent = student.as[JsObject] ++ Json.obj("attr" -> JsObject(updatedAttributes))
requiredStudent
}
requiredStudents.foreach(println)
The output for each student will be as follows:
{"collected":{"field":"field_1"},"attr":{"name":"nametest_name","age":"age17","color":"colorblue"}}
{"collected":{"field":"field_2"},"attr":{"name":"nametest_name2","age":"age18","color":"colorred"}}