jsonplayframework-2.2playframework-2.3

Play 2.x Json transform json keys to camelCase from underscore case


I want to transform a json with underscore case keys to camel case keys.

"{\"first_key\": \"first_value\", \"second_key\": {\"second_first_key\":\"second_first_value\"}}"

to

 "{\"firstKey\": \"first_value\", \"secondKey\": {\"secondFirstKey\":\"second_first_value\"}}"

This is partial code:

val CamelCaseRegex = new Regex("(_.)")
val jsonTransformer = (__).json.update(
  //converts json camel_case field names to Scala camelCase field names  
)
val jsonRet = Json.parse(jsonStr).transform(jsonTransformer)

I have tried several ways in the update method without success.


Solution

  • While it would be nice to do this with just the native Play library, it's a good use-case for Mandubian's Play Json Zipper extension libraries.

    Here's a quick go at this (not exhaustively tested). First you need to add the resolver and library to your build:

    resolvers += "mandubian maven bintray" at "http://dl.bintray.com/mandubian/maven"
    
    libraryDependencies ++= Seq(
      "com.mandubian"     %% "play-json-zipper"    % "1.2"
    )
    

    Then you could try something like this:

    import play.api.libs.json._
    import play.api.libs.json.extensions._
    
    // conversion function borrowed from here:
    // https://gist.github.com/sidharthkuruvila/3154845
    def underscoreToCamel(name: String) = "_([a-z\\d])".r.replaceAllIn(name, {m =>
      m.group(1).toUpperCase
    })
    
    // Update the key, otherwise ignore them...
    // FIXME: The None case shouldn't happen here so maybe we
    // don't need it...
    def underscoreToCamelCaseJs(json: JsValue) = json.updateAllKeyNodes {
      case (path, js) => JsPathExtension.hasKey(path) match {
        case Some(key) => underscoreToCamel(key) -> js
        case None => path.toJsonString -> js
      }
    }
    

    Which on this input:

    val testJson = Json.obj(
      "some_str" -> JsString("foo_bar"),
      "some_obj" -> Json.obj(
        "some_field" -> Json.arr("foo", "bar")
      ),
      "an_int" -> JsNumber(1)
    )
    

    ...produces:

    {
      "someStr" : "foo_bar",
      "someObj" : {
        "someField" : [ "foo", "bar" ]
      },
      "anInt" : 1
    }