Parsing and manipulating json in Scala
Asked Answered
R

1

6

I have this JSON that is returned from a REST-service I'm using.

{
    "id": "6804",
    "signatories": [
        {
            "id": "12125",
            "fields": [
                {
                    "type": "standard",
                    "name": "fstname",
                    "value": "John"
                },
                {
                    "type": "standard",
                    "name": "sndname",
                    "value": "Doe"
                },
                {
                    "type": "standard",
                    "name": "email",
                    "value": "[email protected]"
                },
                {
                    "type": "standard",
                    "name": "sigco",
                    "value": "Company"
                }
            ]
        }
    ]
}

Currently I'm looking into a way to parse this with json4s, iterating over the "fields" array, to be able to change the property "value" of the different objects in there. So far I've tried a few json libs and ended up with json4s.

Json4s allows me to parse the json into a JObject, which I can try extract the "fields" array from.

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

    // parse to JObject
    val data = parse(json)

    // extract the fields into a map
    val fields = data \ "signatories" \ "fields"

    // parse back to JSON
    println(compact(render(fields)))

I've managed to extract a Map like this, and rendered it back to JSON again. What I can't figure out though is, how to loop through these fields and change the property "value" in them?

I've read the json4s documentation but I'm very new to both Scala and it's syntax so I'm having a difficult time.

The question becomes, how do I iterate over a parsed JSON result, to change the property "value"?

Here's the flow I want to achieve.

  1. Parse JSON into iterable object
  2. Loop through and look for certain "names" and change their value, for example fstname, from John to some other name.
  3. Parse it back to JSON, so I can send the new JSON with the updated values back.

I don't know if this is the best way to do this at all, I'd really appreciate input, maybe there's an easier way to do this.

Thanks in advance, Best regards,

Stefan Konno

Reinaldo answered 13/1, 2015 at 13:43 Comment(3)
What's the question?Ayrshire
"how to loop through these fields and change the property "value" in them?". I'll edit the question, to better phrase the question, sorry.Reinaldo
You can always use \\ operator without deserializing object.Winkle
O
6

You can convert the json into an array of case class which is the easiest thing to do. For example: you can have case class for Fields like

case class Field(`type`: String, name: String, value: String)

and you can convert your json into array of fields like read[Array[Field]](json) where json is

 [
            {
                "type": "standard",
                "name": "fstname",
                "value": "John"
            },
            ...
        ]

which will give you an array of fields. Similarly, you can model for your entire Json.

As now you have an array of case classes, its pretty simple to iterate the objects and change the value using case classes copy method.

After that, to convert the array of objects into Json, you can simply use write(objects) (read and write functions of Json4s are available in org.json4s.native.Serialization package.

Update 

To do it without converting it into case class, you can use transformField function

parse(json).transformField{case JField(x, v) if x == "value" && v == JString("Company")=> JField("value1",JString("Company1"))}
Overtime answered 13/1, 2015 at 13:59 Comment(8)
Ok nice, I'll give it try. My plan first was to use a Case Class to convert my json. The json I pasted is only a part of the json I recieve from the REST though, so I couldn't create a case class for like 50 fields. That's why I decided to parse the json with json4s. Can I do this without creating case classes?Reinaldo
I am not sure. I will try after some time, however, IMO, it is better to use a combination of case classes to build a bigger case class then having a big map like structure and Json4s makes it pretty easy to convert a json into a nested case class.Overtime
The thing I can't wrap my head around is, if I somehow manage to convert the signatories fields into a case class, iterate it and change the values. Can I somehow get it back into the original json? Because my json is huge, I'm only showing the relevant parts in the question. But the REST service I'm posting to requires the entire json with updated data and over 50 other fields.Reinaldo
Yes. It should not be a problem. Convert your entire json with 50 fields into a case class with appropriate fields( fields can be simple or complex like list of some other smaller case class). Use the copy method of case class to change the fields as you like. And then convert the instance of main case class into json. I have used json4s to do this for large jsons. It looks quite neat. Each of the case class had less than 5 fields with the main class having around 10 fields.Overtime
Unfortunately you'll hit the 22 limit on parameters for a simple case class which makes it a bit more of a pain for using the generated formatters. You'll need to write you own apply/unapply methods.Rile
I really appreciate the feedback guys, but I'm extremely new to Scala, I'm just trying to write a simple REST-interface with spray.io to give it an evaluation for future reference, but at the moment I'm feeling extremly frustrated with this json problem. I will most likely not be able to complete this due to json manipulation being so damn complicated. Thanks anyway for the feedback though, I marked this as the answer.Reinaldo
@StefanKonno No problem. IMO, a Json with 50 fields is not a good design. It can always be broken down into a nested json having similar attributes, which json4s can convert into a top level case class with one line of code. If that is not possible, then you can use transformField method for simple cases. Otherwise, scala 2.11.x provides case classes with more than 22 fields, however I haven't got any scenario where I will need to have more than 22 fields.Overtime
Yeah you're right, the service I'm working against is quite troublsome. Since I need to send the entire json back to it (just with a few changed fields). So I'm all with you there. I'm gonna give the transformField method a shot. Thanks again for your time.Reinaldo

© 2022 - 2024 — McMap. All rights reserved.