Skip to content

@jsonstrict to always use Play's default format #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 7, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ If the case class contains 2 fields or more, Play's [JSON macro inception](http:

This is often more convenient than Play's default format ```{"name": "San Francisco"}```.

If you would rather stick to Play's default format even for single field case classes, you can use ```@jsonstrict``` instead of ```@json```.

#Installation

If you're using Play (version 2.1 or higher) with SBT, you should add the following settings to your build:
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ organization := "com.kifi"

name := "json-annotation"

version := "0.1"
version := "0.2"

scalaVersion := "2.11.1"

Expand Down
31 changes: 25 additions & 6 deletions src/main/scala/com/kifi/macros/JsonFormatAnnotation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import scala.annotation.StaticAnnotation

import CrossVersionDefs._

object jsonMacroInstance extends jsonMacro(false)
object jsonStrictMacroInstance extends jsonMacro(true)

/**
* "@json" macro annotation for case classes
*
Expand All @@ -21,10 +24,24 @@ import CrossVersionDefs._
* then A(4) will be serialized as '4' instead of '{"value": 4}'.
*/
class json extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro jsonMacro.impl
def macroTransform(annottees: Any*): Any = macro jsonMacroInstance.impl
}

/**
* "@jsonstrict" macro annotation for case classes
*
* Same as "@json" annotation, except that it always uses the default Play formatter.
* For example, if A is defined as:
*
* case class A(value: Int)
*
* then A(4) will be serialized as '{"value": 4}'.
*/
class jsonstrict extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro jsonStrictMacroInstance.impl
}

object jsonMacro {
class jsonMacro(isStrict: Boolean) {
def impl(c: CrossVersionContext)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._

Expand All @@ -40,8 +57,8 @@ object jsonMacro {
def jsonFormatter(className: TypeName, fields: List[ValDef]) = {
fields.length match {
case 0 => c.abort(c.enclosingPosition, "Cannot create json formatter for case class with no fields")
case 1 =>
// Only one field, use the serializer for the field
case 1 if !isStrict => {
// use the serializer for the field
q"""
implicit val jsonAnnotationFormat = {
import play.api.libs.json._
Expand All @@ -51,9 +68,11 @@ object jsonMacro {
)
}
"""
case _ =>
// More than one field, use Play's macro
}
case _ => {
// use Play's macro
q"implicit val jsonAnnotationFormat = play.api.libs.json.Json.format[$className]"
}
}
}

Expand Down
30 changes: 27 additions & 3 deletions src/test/scala/com/kifi/macros/JsonFormatAnnotationTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import org.specs2.mutable.Specification
import play.api.libs.json._

@json case class City(name: String)

@json case class Person(name: String, age: Int)

@jsonstrict case class City2(name: String)
@jsonstrict case class Person2(name: String, age: Int)

class JsonFormatAnnotationTest extends Specification {

"@json annotation" should {
Expand All @@ -16,7 +18,7 @@ class JsonFormatAnnotationTest extends Specification {
val city = City("San Francisco")
val json = Json.toJson(city)
json === JsString("San Francisco")
Json.fromJson[City](json) === JsSuccess(city)
Json.fromJson[City](json).asOpt must beSome(city)
}

"create correct formatter for case class with >= 2 fields" in {
Expand All @@ -27,7 +29,29 @@ class JsonFormatAnnotationTest extends Specification {
"name" -> "Victor Hugo",
"age" -> 46
)
Json.fromJson[Person](json) === JsSuccess(person)
Json.fromJson[Person](json).asOpt must beSome(person)
}
}

"@jsonstrict annotation" should {

"create correct formatter for case class with 1 field" in {

val city = City2("San Francisco")
val json = Json.toJson(city)
json === Json.obj("name" -> "San Francisco")
Json.fromJson[City2](json).asOpt must beSome(city)
}

"create correct formatter for case class with >= 2 fields" in {

val person = Person2("Victor Hugo", 46)
val json = Json.toJson(person)
json === Json.obj(
"name" -> "Victor Hugo",
"age" -> 46
)
Json.fromJson[Person2](json).asOpt must beSome(person)
}
}
}