1
votes

I've been working with play 2.1's JSON Library, specifically combinators and transformers: http://www.playframework.com/documentation/2.1.4/ScalaJsonCombinators

I have a use case where I have a large json structure (100+ keys). I need to update 20-30 of these values. In my current understanding of the library, there are a few different ways I can achieve this:

1.Use an update transformer with "andThen" combinators:

val jsonTransformer = (__ \ 'key2).json.pickBranch(
  (__ \ 'key21).json.update( 
    of[JsNumber].map{ case JsNumber(nb) => JsNumber(nb + 10) }
  ) andThen 
  (__ \ 'key23).json.update( 
    of[JsArray].map{ case JsArray(arr) => JsArray(arr :+ JsString("delta")) }
  )
)

I would have to chain 20+ "andThen" combinators...there's gotta be a better way, right? It would be nice to have a transformer that takes a HashMap of values, and replaces the key/values of the hashmap with the ones in that particular json branch. Can this be done?

2.Another approach I thought of is creating a JsObject beforehand, and then just update the branch with one transform call:

val jsonObject = Json.toJson(
  Map(
    "users" -> Seq(
      toJson(
        Map(
          "name" -> toJson("Bob"),
          "age" -> toJson(31),
          "email" -> toJson("[email protected]")
        )
      ),
      toJson(
        Map(
          "name" -> toJson("Kiki"),
          "age" -> toJson(25),
          "email" -> JsNull
        )
      )
    )
  )
)

This is doable, but again could get quite big.

3.Define my own class for this data with implicit Reads/Writes formatters. This would require me to build OO hierarchies and seems against some of the principles established by the library author with JSON-coast-to-coast: http://www.playframework.com/documentation/2.1.2/ScalaJsonTransformers .

If you have any thoughts on which method would be best, I'd be interested to hear. Thanks!

1
have a look at JsZipper an extension to play-json by play-json author to make this kind of manipulations easier mandubian.com/2013/05/01/JsZipperJean
That's great - just what I was looking for! Thanks.Chris Ridmann

1 Answers

1
votes

I used lift-json (https://github.com/lift/lift/tree/master/framework/lift-base/lift-json/) with Play! Framework (mixin LiftJson trait available at: https://github.com/tototoshi/lift-json-play-module) for a similar use case as the ScalaJsonCombinators approach seemed complicated to maintain.

You can create case classes with only the fields that you think change (if all fields are changeable then create case classes with all JSON node names according to the JSON structure) and LiftJson provides functionality for mapping JSON strings to the case classes. (No custom TypeHints are required unless you have to write custom serializer or deserializer. Just use the DefaultFormats.)

Once you have deserialized your JSON to case class objects, you can use the copy method on the case classes to replace the fields you want and serialize it back to JSON. This seemed to be more maintainable solution rather than calling the JSON node names directly all over the code.