Search code examples
jsonscalajson4s

replace object in Json AST with json4s.replace


I have this json, I've been parsing and replacing fields with json4s, the json looks like this:

{
    "id": "6988",
    "signatories": [
        {
            "fields": [
                {
                    "name": "fstname",
                    "value": "Bruce"
                },
                {
                    "name": "sndname",
                    "value": "Lee"
                },
                {
                    "name": "email",
                    "value": "bruce.lee@company.com"
                },
                {
                    "name": "sigco",
                    "value": "Company"
                },
                {
                    "name": "mobile",
                    "value": "0760000000"
                }
            ]
        },
        {
            "fields": [
                {
                    "name": "fstname",
                    "value": ""
                },
                {
                    "name": "sndname",
                    "value": ""
                },
                {
                    "name": "email",
                    "value": ""
                },
                {
                    "name": "mobile",
                    "value": ""
                },
                {
                    "name": "sigco",
                    "value": ""
                }
            ]
        }
    ]
}

The second array called "fields", that's the one I want to replace the empty strings with actual string values. I have been doing this with json4s transformField function, by first parsing the json into a JObject, then transforming the JObject with the new values.

val a = parse(json)

// transform the second occurance of fields (fields[1])
val v = a.\("signatories").\("fields")(1).transform {
      // Each JArray is made of objects. Find fields in the object with key as name and value as fstname
      case obj: JObject => obj.findField(_.equals(JField("name", JString("fstname")))) match {
        case None => obj //Didn't find the field. Return the same object back to the array
        // Found the field. Change the value
        case Some(x) =>
          obj.transformField { case JField(k, v) if k == "value" => JField(k, JString("New name")) }
      }
    }

Now I got my original parsed json "a", and I got my new JArray with the updated fields in "v". The last thing I need to do is to merge the new values into the original JObject "a". I tried this with replace, with no luck.

val merged = a.replace(List("fields"), v)

Question:

  1. How do I get replace() to replace my second occurance of the fields array?
  2. Is there a better way to do this? Suggestions very appreciated.

Here is a runnable version of the code in it's entirety with example prints of the json as well: http://pastebin.com/e0xmxqFF


Solution

  • Since you are very specific about the field you want to update I think the most straightforward solution would be to use mapField:

     val merged = a mapField {
        case ("signatories", JArray(arr)) => ("signatories", JArray(arr.updated(1, JObject(JField("fields", v)))))
        case other => other
      }
    

    replace can't be used as far as I can see, since you would need to pass it an index, which would not work, since replace expects its first argument to be a List of fields name.