Skip to content

Commit decbf31

Browse files
committed
@jsonstrict to always use Play's default format
1 parent 2ca611c commit decbf31

File tree

3 files changed

+54
-9
lines changed

3 files changed

+54
-9
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ If the case class contains 2 fields or more, Play's [JSON macro inception](http:
2929

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

32+
If you would rather stick to Play's default format even for single field case classes, you can use ```@jsonstrict``` instead of ```@json```.
33+
3234
#Installation
3335

3436
If you're using Play (version 2.1 or higher) with SBT, you should add the following settings to your build:

src/main/scala/com/kifi/macros/JsonFormatAnnotation.scala

+25-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import scala.annotation.StaticAnnotation
66

77
import CrossVersionDefs._
88

9+
object jsonMacroInstance extends jsonMacro(false)
10+
object jsonStrictMacroInstance extends jsonMacro(true)
11+
912
/**
1013
* "@json" macro annotation for case classes
1114
*
@@ -21,10 +24,24 @@ import CrossVersionDefs._
2124
* then A(4) will be serialized as '4' instead of '{"value": 4}'.
2225
*/
2326
class json extends StaticAnnotation {
24-
def macroTransform(annottees: Any*): Any = macro jsonMacro.impl
27+
def macroTransform(annottees: Any*): Any = macro jsonMacroInstance.impl
28+
}
29+
30+
/**
31+
* "@jsonstrict" macro annotation for case classes
32+
*
33+
* Same as "@json" annotation, except that it always uses the default Play formatter.
34+
* For example, if A is defined as:
35+
*
36+
* case class A(value: Int)
37+
*
38+
* then A(4) will be serialized as '{"value": 4}'.
39+
*/
40+
class jsonstrict extends StaticAnnotation {
41+
def macroTransform(annottees: Any*): Any = macro jsonStrictMacroInstance.impl
2542
}
2643

27-
object jsonMacro {
44+
class jsonMacro(isStrict: Boolean) {
2845
def impl(c: CrossVersionContext)(annottees: c.Expr[Any]*): c.Expr[Any] = {
2946
import c.universe._
3047

@@ -40,8 +57,8 @@ object jsonMacro {
4057
def jsonFormatter(className: TypeName, fields: List[ValDef]) = {
4158
fields.length match {
4259
case 0 => c.abort(c.enclosingPosition, "Cannot create json formatter for case class with no fields")
43-
case 1 =>
44-
// Only one field, use the serializer for the field
60+
case 1 if !isStrict => {
61+
// use the serializer for the field
4562
q"""
4663
implicit val jsonAnnotationFormat = {
4764
import play.api.libs.json._
@@ -51,9 +68,11 @@ object jsonMacro {
5168
)
5269
}
5370
"""
54-
case _ =>
55-
// More than one field, use Play's macro
71+
}
72+
case _ => {
73+
// use Play's macro
5674
q"implicit val jsonAnnotationFormat = play.api.libs.json.Json.format[$className]"
75+
}
5776
}
5877
}
5978

src/test/scala/com/kifi/macros/JsonFormatAnnotationTest.scala

+27-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import org.specs2.mutable.Specification
44
import play.api.libs.json._
55

66
@json case class City(name: String)
7-
87
@json case class Person(name: String, age: Int)
98

9+
@jsonstrict case class City2(name: String)
10+
@jsonstrict case class Person2(name: String, age: Int)
11+
1012
class JsonFormatAnnotationTest extends Specification {
1113

1214
"@json annotation" should {
@@ -16,7 +18,7 @@ class JsonFormatAnnotationTest extends Specification {
1618
val city = City("San Francisco")
1719
val json = Json.toJson(city)
1820
json === JsString("San Francisco")
19-
Json.fromJson[City](json) === JsSuccess(city)
21+
Json.fromJson[City](json).asOpt must beSome(city)
2022
}
2123

2224
"create correct formatter for case class with >= 2 fields" in {
@@ -27,7 +29,29 @@ class JsonFormatAnnotationTest extends Specification {
2729
"name" -> "Victor Hugo",
2830
"age" -> 46
2931
)
30-
Json.fromJson[Person](json) === JsSuccess(person)
32+
Json.fromJson[Person](json).asOpt must beSome(person)
33+
}
34+
}
35+
36+
"@jsonstrict annotation" should {
37+
38+
"create correct formatter for case class with 1 field" in {
39+
40+
val city = City2("San Francisco")
41+
val json = Json.toJson(city)
42+
json === Json.obj("name" -> "San Francisco")
43+
Json.fromJson[City2](json).asOpt must beSome(city)
44+
}
45+
46+
"create correct formatter for case class with >= 2 fields" in {
47+
48+
val person = Person2("Victor Hugo", 46)
49+
val json = Json.toJson(person)
50+
json === Json.obj(
51+
"name" -> "Victor Hugo",
52+
"age" -> 46
53+
)
54+
Json.fromJson[Person2](json).asOpt must beSome(person)
3155
}
3256
}
3357
}

0 commit comments

Comments
 (0)