3

I'm giving Scala a try, and in particular the json4s lib in order to manipulate some json. I'm having a hard time with the syntax of both Scala and json4s, thought I'd ask you guys.

I have this json, which I need to update some fields on, and send back intact in it's entirety to a service. The json looks like this:

{
    "id": "6804",
    "signatories": [
        {
            "id": "12125",
            "fields": [
                {
                    "type": "standard",
                    "name": "fstname",
                    "value": "John"
                },
                {
                    "type": "standard",
                    "name": "sndname",
                    "value": "Doe"
                },
                {
                    "type": "standard",
                    "name": "email",
                    "value": "john.doe@somwhere.com"
                },
                {
                    "type": "standard",
                    "name": "sigco",
                    "value": "Company"
                }
            ]
        }
    ]
}

I am using json4s to parse this into a JArray, like this:

import org.json4s._
import org.json4s.native.JsonMethods._

val data = parse(json)
val fields = (data \ "signatories" \ "fields")

This gives me a JArray containing all fields: (So very sorry for formatting)

JArray(List(JObject(List((type,JString(standard)), (name,JString(fstname)), (value,JString(John)))), JObject(List((type,JString(standard)), (name,JString(sndname)), (value,JString(Doe)))), JObject(List((type,JString(standard)), (name,JString(email)), (value,JString(john.doe@somwhere.com)))), JObject(List((type,JString(standard)), (name,JString(sigco)), (value,JString(Company))))))

The problem I'm faced with now, is:

how do I find every fields property "name", and change it (transform), into a new value?

For example (I know this isn't how it works in Scala most likely, but you'll get the idea)

foreach(field in fields) {
     if(field.name == 'fstname') {
          field.value = "Bruce"
     }
}
Stefan Konno
  • 1,337
  • 2
  • 16
  • 28
  • It looks like it is a good candidate for the use of lenses. Check out http://stackoverflow.com/questions/17255228/using-lenses-on-scala-regular-classes/17255837#17255837 – Jérôme Mahuet Jan 14 '15 at 08:24
  • BEWARE: [json4s is vulnerable under DoS/DoW attacks!](https://github.com/json4s/json4s/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+denial) – Andriy Plokhotnyuk Jul 04 '22 at 08:31

2 Answers2

2

You can try

val a = JArray(List(JObject(....))) // Same as your JArray<pre><code>   

a.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("Bruce")) } 
}
}    

Result -


res0: org.json4s.JValue = JArray(List(JObject(List((typ,JString(standard)), (name,JString(fstname)), (value,JString(Bruce)))), JObject(List((typ,JString(standard)), (name,JString(sndname)), (
ring(Doe)))), JObject(List((typ,JString(standard)), (name,JString(email)), (value,JString(john.doe@somwhere.com)))), JObject(List((typ,JString(standard)), (name,JString(sigco)), (value,JStrin
)))))) 
mohit
  • 4,968
  • 1
  • 22
  • 39
  • I tried printing println(k.toString()), println(v.toString()) in the case Some(x), and it gives me JString(Bruce), but when the case matching is done, I try to print my object a, to check if the value for fstname is indeed equal to "Bruce", but the object still has "John". (name,JString(fstname)), (value,JString(John)))). It seems the matching occurs, but then it's not saved into the object. – Stefan Konno Jan 14 '15 at 08:52
  • I have added the final result. It definitely works. Try printing after the transformation is completed. – mohit Jan 14 '15 at 09:00
  • I feel like a complete idiot, because as you say, it should work, and it looks good, but I can't seem to get it to work, here's a pastebin of the code I'm running. I must be missing something fundamental here, perhaps the println is run before the case matching is done? http://pastebin.com/3XYW4Xgr – Stefan Konno Jan 14 '15 at 09:16
  • Pastebin - http://pastebin.com/up5ZTg0x. Maybe there is a simpler way to do this. – mohit Jan 14 '15 at 09:36
  • I combined your last pastebin with the merge function in json4s.jackson, and I managed to get it to work. I can't thank you enough for your help, you're a life saver. I pasted my final code here. http://pastebin.com/kChN7m1V – Stefan Konno Jan 14 '15 at 09:52
  • Glad to be of help :) – mohit Jan 14 '15 at 09:58
  • Sorry to bother you again, but it seems I can't get it to work quite yet. I've been trying for a day now :( In the last pastebin I pasted, I used json4s's merge to merge the two JObjects together, but it seems after the merge, I'm left with only the fields array. The merge can't figure out how to merge the new val v, into the bigger object a, it just returns the fields array, and I lose the rest of the object. Then when I transform it back to json, I lost everything but the fields array. Here's the example. http://pastebin.com/PsLLPDfr – Stefan Konno Jan 15 '15 at 09:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/68865/discussion-between-stefan-konno-and-mohit). – Stefan Konno Jan 15 '15 at 09:54
0

Short solution:

val modified = data transformField {
  case JField("name", JString("fstname")) => ("name", "Bruce")
}

Check more examples in github.

Camilo Silva
  • 8,283
  • 4
  • 41
  • 61