4

Using the CIRCE library & Cats, it would be incredibly useful to be able to transform all the string values of an arbitrary Json object such as

{
  "topLevelStr" : "topLevelVal", 
  "topLevelInt" : 123, 
  "nested" : { "nestedStr" : "nestedVal" },
  "array" : [
    { "insideArrayStr" : "insideArrayVal1", "insideArrayInt" : 123},
    { "insideArrayStr" : "insideArrayVal2", "insideArrayInt" : 123}
   ]
}

Is it possble to transform all string values (topLevelVal, nestedVal, insideArrayVal1, insideArrayVal2) to upper case (or any arbitrary string transformation for that matter)?

Tobias Roland
  • 1,182
  • 1
  • 13
  • 35

1 Answers1

3

You can write recursive function by yourself. It should be something like that:

import io.circe.{Json, JsonObject}
import io.circe.parser._


def transform(js: Json, f: String => String): Json = js
  .mapString(f)
  .mapArray(_.map(transform(_, f)))
  .mapObject(obj => {
    val updatedObj = obj.toMap.map {
      case (k, v) => f(k) -> transform(v, f)
    }
    JsonObject.apply(updatedObj.toSeq: _*)
  })

val jsonString =
  """
    |{
    |"topLevelStr" : "topLevelVal",
    |"topLevelInt" : 123, 
    | "nested" : { "nestedStr" : "nestedVal" },
    | "array" : [
    |   {
    |      "insideArrayStr" : "insideArrayVal1",
    |      "insideArrayInt" : 123
    |   }
    |  ]
    |}
  """.stripMargin

val json: Json = parse(jsonString).right.get
println(transform(json, s => s.toUpperCase))
Nikita
  • 4,435
  • 3
  • 24
  • 44
  • 1
    Very nice. It shortened down my solution immensely, so now it's simply: `def transformStringValues(f: String => String, json: Json): Json = json.mapString(f).mapArray(a => a.map(transformStringValues(f, _))).mapObject(obj => JsonObject(obj.toMap.mapValues(transformStringValues(f, _)).toSeq :_*))` (can't format it nicely in the comments, but it works as a oneliner as well) – Tobias Roland Jul 26 '18 at 16:13