Search code examples
jsonscalacirce

Transform all keys from `underscore` to `camel case` of json objects in circe


Origin

{
  "first_name" : "foo",
  "last_name" : "bar",
  "parent" : {
    "first_name" : "baz",
    "last_name" : "bazz",
  }
}

Expected

 {
      "firstName" : "foo",
      "lastName" : "bar",
      "parent" : {
        "firstName" : "baz",
        "lastName" : "bazz",
      }
    }

How can I transform all keys of json objects ?


Solution

  • Here's how I'd write this. It's not as concise as I'd like, but it's not terrible:

    import cats.free.Trampoline
    import cats.std.list._
    import cats.syntax.traverse._
    import io.circe.{ Json, JsonObject }
    
    /**
     * Helper method that transforms a single layer.
     */
    def transformObjectKeys(obj: JsonObject, f: String => String): JsonObject =
      JsonObject.fromIterable(
        obj.toList.map {
          case (k, v) => f(k) -> v
        }
      )
    
    def transformKeys(json: Json, f: String => String): Trampoline[Json] =
      json.arrayOrObject(
        Trampoline.done(json),
        _.traverse(j => Trampoline.suspend(transformKeys(j, f))).map(Json.fromValues),
        transformObjectKeys(_, f).traverse(obj => Trampoline.suspend(transformKeys(obj, f))).map(Json.fromJsonObject)
      )
    

    And then:

    import io.circe.literal._
    
    val doc = json"""
    {
      "first_name" : "foo",
      "last_name" : "bar",
      "parent" : {
        "first_name" : "baz",
        "last_name" : "bazz"
      }
    }
    """
    
    def sc2cc(in: String) = "_([a-z\\d])".r.replaceAllIn(in, _.group(1).toUpperCase)
    

    And finally:

    scala> import cats.std.function._
    import cats.std.function._
    
    scala> transformKeys(doc, sc2cc).run
    res0: io.circe.Json =
    {
      "firstName" : "foo",
      "lastName" : "bar",
      "parent" : {
        "firstName" : "baz",
        "lastName" : "bazz"
      }
    }
    

    We probably should have some way of recursively applying a Json => F[Json] transformation like this more conveniently.