2

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?

cchantep
  • 8,797
  • 3
  • 25
  • 39
  • is attribute an array of json object or just one single json object? In your input json, you've created as an array and in the expected output you have just one single attr object for each student? – Sunil Kumar Mar 01 '20 at 01:09
  • Sorry for that. I fixed the output. Can you check again? – labyrinthdeux Mar 01 '20 at 08:36

1 Answers1

2

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
}

Old Answer

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"}}
Sunil Kumar
  • 582
  • 1
  • 8
  • 27