scalajson4s

update json values with json4s for a json path


I want to update json values in my Json. I tried using transform field and replace methods but each has a problem which blocks me to proceed . So I am looking for some help. I have to make a method which takes a List of Map argument , the Key in that Map Corresponds to the Json Path and the value be the value to be updated for example:

def jsonFieldUpdater( SeaarchKey :  List(Map(key, VALUE ) )

So in the map I have a Json Path like [\inner\age, 30 ] which acts as a key ; my method reads it and updates that field not all the age keys. See below example

{"name":"luca", "id": "1q2w3e4r5t", "age": 26, "inner": { "age": 27 }, "url":"http://www.nosqlnocry.wordpress.com"}

I am bit stuck here as if i use Use transformfield method it updates all values which has the key as age, I dont want that I want to go to a specific path and transform it. My code block is :

import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods._
object Json4sTest {  
  def main(arg: Array[String]) {
    //val source = scala.io.Source.fromFile("file.txt")
    //val lines = try source.mkString finally source.close()
    var json = parse("""{"name":"luca", "id": "1q2w3e4r5t", "age": 26, "inner": { "age": 27 }, "url":"http://    www.nosqlnocry.wordpress.com"}""")
    println(pretty(json))
    //JObject(List(JField("foo", JObject(List(JField("bar", JInt(1))))))).replace("foo" :: "bar" :: Nil, JString("baz"))
    json = json.replace("inner" :: "age" :: Nil, 29)
    json = json transformField {
    case ("name", _) => ("NAME", JString("Tuca"))
    case ("age", JInt(age)) => ("age", JInt(age+1))
    //case ("checkIn", date) => ("checkIn", JString(df.print(now.plusDays(deltaIn))))
    }
    println(pretty(json))

  }
}

The I came across the replace method which does the task , of replacement of a value. But then it looks like this : json = json.replace("inner" :: "age" :: Nil, 29) I am not able to think how can I conform to this format, when i just receive a map which has a Json path and an value which has to be updated. Any other idea to achieve the same ?

I tried the solution by n4to4 but as i do the assignment of List of Map to a variable I get the error , for example:

val a = List(Map("inner/age" -> 35, "age" -> 27, "name" -> "foo"))
jsonFieldUpdater(json, a )
// error : type mismatch; found :    List[scala.collection.immutable.Map[String,Any]] required: List[Map[String,org.json4s.JValue]] (which expands to) List[Map[String,org.json4s.JsonAST.JValue]]

Solution

  • It doesn't have to be List of Map probably...?

    import org.json4s._
    import org.json4s.JsonDSL._
    import org.json4s.jackson.JsonMethods._
    
    object Json4sTest {
      def main(arg: Array[String]) {
        var json = parse("""{"name":"luca", "id": "1q2w3e4r5t", "age": 26, "inner": { "age": 27 }, "url":"http://    www.nosqlnocry.wordpress.com"}""")
        println(pretty(json))
    
        val a: List[Map[String, JValue]] = List(Map("inner/age" -> 35, "age" -> 27), Map("name" -> "foo"))
        val r = jsonFieldUpdater(json, a)
        println(pretty(r))
    
        val b = List(Updater("inner/age", 35), Updater("age", 27), Updater("name", "foo"))
        val s = jsonFieldUpdater2(json, b)
        println(pretty(s))
      }
    
      def jsonFieldUpdater(json: JValue, list: List[Map[String, JValue]]): JValue =
        list.foldLeft(json) { case (json, kvs) =>
          kvs.foldLeft(json) { case (json, (key, value)) =>
            json.replace(key.split("/").toList, value)
          }
        }
    
      case class Updater(key: String, value: JValue) {
        def path: List[String] = key.split("/").toList
      }
    
      def jsonFieldUpdater2(json: JValue, list: List[Updater]): JValue =
        list.foldLeft(json) { case (json, u) =>
          json.replace(u.path, u.value)
        }
    }