From 3da54a9e315a74d0276fe397dc31456ade1295e5 Mon Sep 17 00:00:00 2001 From: toshi Date: Mon, 19 Jan 2015 20:13:10 +0900 Subject: [PATCH] init --- .gitignore | 6 +++++ build.sbt | 12 +++++++++ project/build.properties | 4 +++ .../tototoshi/play/json/JsonNaming.scala | 25 +++++++++++++++++++ .../tototoshi/play/json/StringUtil.scala | 19 ++++++++++++++ .../tototoshi/play/json/JsonNamingSuite.scala | 25 +++++++++++++++++++ .../tototoshi/play/json/StringUtilSuite.scala | 18 +++++++++++++ 7 files changed, 109 insertions(+) create mode 100644 .gitignore create mode 100644 build.sbt create mode 100644 project/build.properties create mode 100644 src/main/scala/com/github/tototoshi/play/json/JsonNaming.scala create mode 100644 src/main/scala/com/github/tototoshi/play/json/StringUtil.scala create mode 100644 src/test/scala/com/github/tototoshi/play/json/JsonNamingSuite.scala create mode 100644 src/test/scala/com/github/tototoshi/play/json/StringUtilSuite.scala diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93f7430 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/RUNNING_PID +/logs/ +/project/*-shim.sbt +/project/project/ +/project/target/ +/target/ diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..e91be94 --- /dev/null +++ b/build.sbt @@ -0,0 +1,12 @@ +name := """play-json-naming""" + +version := "1.0" + +scalaVersion := "2.11.5" + +crossScalaVersions := Seq("2.10.4", "2.11.5") + +libraryDependencies ++= Seq( + "com.typesafe.play" %% "play-json" % "2.3.7" % "provided", + "org.scalatest" %% "scalatest" % "2.1.6" % "test" +) diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..9ec1aba --- /dev/null +++ b/project/build.properties @@ -0,0 +1,4 @@ +#Activator-generated Properties +#Mon Jan 19 19:48:42 JST 2015 +template.uuid=a855816c-0367-44ba-9adb-6a903f6ad599 +sbt.version=0.13.7 diff --git a/src/main/scala/com/github/tototoshi/play/json/JsonNaming.scala b/src/main/scala/com/github/tototoshi/play/json/JsonNaming.scala new file mode 100644 index 0000000..dd1c0fa --- /dev/null +++ b/src/main/scala/com/github/tototoshi/play/json/JsonNaming.scala @@ -0,0 +1,25 @@ +package com.github.tototoshi.play.json + +import play.api.libs.json._ + +object JsonNaming { + + private def mapKeys[A, B](m: Seq[(A, B)])(f: A => A): Seq[(A, B)] = + m.map { case (k, v) => (f(k), v) } + + def snakecase[T](format: Format[T]): Format[T] = new Format[T] { + def reads(json: JsValue): JsResult[T] = { + format.reads(json match { + case obj: JsObject => JsObject(mapKeys(obj.fields)(StringUtil.camelcase)) + case x => x + }) + } + + def writes(o: T): JsValue = { + format.writes(o) match { + case obj: JsObject => JsObject(mapKeys(obj.fields)(StringUtil.snakecase)) + case x => x + } + } + } +} diff --git a/src/main/scala/com/github/tototoshi/play/json/StringUtil.scala b/src/main/scala/com/github/tototoshi/play/json/StringUtil.scala new file mode 100644 index 0000000..f28cc7a --- /dev/null +++ b/src/main/scala/com/github/tototoshi/play/json/StringUtil.scala @@ -0,0 +1,19 @@ +package com.github.tototoshi.play.json + +import play.api.libs.json._ + +private[json] object StringUtil { + + def camelcase(s: String): String = (s.split("_").toList match { + case head :: tail => head :: tail.map(_.capitalize) + case x => x + }).mkString + + def snakecase(s: String): String = s.foldLeft(new StringBuilder) { + case (s, c) if Character.isUpperCase(c) => + s.append("_").append(Character.toLowerCase(c)) + case (s, c) => + s.append(c) + }.toString + +} diff --git a/src/test/scala/com/github/tototoshi/play/json/JsonNamingSuite.scala b/src/test/scala/com/github/tototoshi/play/json/JsonNamingSuite.scala new file mode 100644 index 0000000..683da73 --- /dev/null +++ b/src/test/scala/com/github/tototoshi/play/json/JsonNamingSuite.scala @@ -0,0 +1,25 @@ +package com.github.tototoshi.play.json + +import org.scalatest._ +import play.api.libs.json._ + +case class Name(firstName: String, lastName: String) +case class User(id: Int, nameData: Name) + +class JsonNamingSuite extends FunSuite with ShouldMatchers { + + + implicit val nameFormat = JsonNaming.snakecase(Json.format[Name]) + implicit val userFormat = JsonNaming.snakecase(Json.format[User]) + + val jsonString = """{"id":1,"name_data":{"first_name":"Toshiyuki","last_name":"Takahashi"}}""" + + test("read") { + Json.parse(jsonString).validate[User].get should be(User(1, Name("Toshiyuki", "Takahashi"))) + } + + test("write") { + Json.toJson(User(1, Name("Toshiyuki", "Takahashi"))) should be(Json.parse(jsonString)) + } + +} diff --git a/src/test/scala/com/github/tototoshi/play/json/StringUtilSuite.scala b/src/test/scala/com/github/tototoshi/play/json/StringUtilSuite.scala new file mode 100644 index 0000000..a39ead1 --- /dev/null +++ b/src/test/scala/com/github/tototoshi/play/json/StringUtilSuite.scala @@ -0,0 +1,18 @@ +package com.github.tototoshi.play.json + +import org.scalatest._ +import play.api.libs.json._ + +class StringUtilSuite extends FunSuite with ShouldMatchers { + + test("camelCase -> snake_case") { + StringUtil.snakecase("play") should be("play") + StringUtil.snakecase("playJson") should be("play_json") + } + + test("snake_case -> camelCase") { + StringUtil.camelcase("play") should be("play") + StringUtil.camelcase("play_json") should be("playJson") + } + +}