Skip to content

Commit 658fcae

Browse files
Simplify AnnotationIntrospector
1 parent 2ff4a7a commit 658fcae

File tree

9 files changed

+181
-55
lines changed

9 files changed

+181
-55
lines changed

Berksfile

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
source 'https://supermarket.chef.io'
22

3+
cookbook 'apt'
34
cookbook 'git'
45
cookbook 'java'
56
cookbook 'maven'

Berksfile.lock

+9-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
DEPENDENCIES
2+
apt
23
git
34
java
45
maven
@@ -7,32 +8,27 @@ DEPENDENCIES
78
GRAPH
89
7-zip (1.0.2)
910
windows (>= 1.2.2)
11+
apt (2.7.0)
1012
ark (0.9.0)
1113
7-zip (>= 0.0.0)
1214
windows (>= 0.0.0)
13-
build-essential (2.2.2)
14-
chef_handler (1.1.6)
15+
build-essential (2.2.3)
16+
chef_handler (1.2.0)
1517
dmg (2.2.2)
16-
git (4.1.0)
18+
git (4.2.2)
1719
build-essential (>= 0.0.0)
1820
dmg (>= 0.0.0)
19-
runit (>= 1.0)
2021
windows (>= 0.0.0)
21-
yum (~> 3.0)
2222
yum-epel (>= 0.0.0)
2323
java (1.31.0)
2424
maven (1.3.0)
2525
ark (~> 0.4)
2626
java (~> 1.13)
2727
windows (>= 0.0.0)
28-
runit (1.5.18)
29-
build-essential (>= 0.0.0)
30-
yum (~> 3.0)
31-
yum-epel (>= 0.0.0)
3228
sbt-extras (0.4.0)
3329
java (>= 0.0.0)
34-
windows (1.36.6)
30+
windows (1.37.0)
3531
chef_handler (>= 0.0.0)
36-
yum (3.5.3)
37-
yum-epel (0.6.0)
38-
yum (~> 3.0)
32+
yum (3.6.3)
33+
yum-epel (0.6.2)
34+
yum (~> 3.2)

Vagrantfile

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
2525
end
2626

2727
config.vm.provision :chef_zero do |chef|
28+
chef.add_recipe :apt
2829
chef.add_recipe :git
2930
chef.add_recipe :maven
3031
chef.add_recipe :'sbt-extras'

build.sbt

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ libraryDependencies ++= Seq(
3030
"com.fasterxml.jackson.core" % "jackson-core" % "2.6.0-rc4",
3131
"com.fasterxml.jackson.core" % "jackson-annotations" % "2.6.0-rc4",
3232
"com.fasterxml.jackson.core" % "jackson-databind" % "2.6.0-SNAPSHOT",
33-
"com.thoughtworks.paranamer" % "paranamer" % "2.6",
33+
"com.fasterxml.jackson.module" % "jackson-module-paranamer" % "2.6.0-rc4" exclude("com.fasterxml.jackson.core","jackson-databind"),
3434
// test dependencies
3535
"com.fasterxml.jackson.datatype" % "jackson-datatype-joda" % "2.6.0-rc4" % "test" exclude("com.fasterxml.jackson.core","jackson-databind"),
3636
"com.fasterxml.jackson.datatype" % "jackson-datatype-guava" % "2.6.0-rc4" % "test" exclude("com.fasterxml.jackson.core","jackson-databind"),

src/main/java/com/fasterxml/jackson/module/scala/JsonScalaEnumeration.java

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.fasterxml.jackson.module.scala;
22

3+
import com.fasterxml.jackson.annotation.JacksonAnnotation;
34
import com.fasterxml.jackson.core.type.TypeReference;
45
import scala.Enumeration;
56

@@ -10,6 +11,7 @@
1011

1112
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
1213
@Retention(RetentionPolicy.RUNTIME)
14+
@JacksonAnnotation
1315
public @interface JsonScalaEnumeration {
1416
Class<? extends TypeReference<? extends Enumeration>> value();
1517
}

src/main/scala/com/fasterxml/jackson/module/scala/introspect/ScalaAnnotationIntrospectorModule.scala

+16-38
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
package com.fasterxml.jackson.module.scala
1+
package com.fasterxml.jackson
2+
package module
3+
package scala
24
package introspect
35

4-
import com.fasterxml.jackson.annotation.JsonCreator
5-
import com.fasterxml.jackson.databind.PropertyName
6-
import com.fasterxml.jackson.databind.`type`.ClassKey
7-
import com.fasterxml.jackson.databind.introspect._
8-
import com.fasterxml.jackson.databind.util.LRUMap
9-
import com.fasterxml.jackson.module.scala.util.Implicits._
6+
import annotation.JsonCreator
7+
import databind.`type`.ClassKey
8+
import databind.introspect._
9+
import databind.util.LRUMap
10+
import paranamer.ParanamerAnnotationIntrospector
1011

11-
import scala.collection.JavaConverters._
12-
import scala.language.postfixOps
12+
import util.Implicits._
1313

14-
object ScalaAnnotationIntrospector extends JacksonAnnotationIntrospector
14+
object ScalaAnnotationIntrospector extends NopAnnotationIntrospector
1515
{
1616
private [this] val _descriptorCache = new LRUMap[ClassKey, BeanDescriptor](16, 100)
1717

@@ -26,9 +26,6 @@ object ScalaAnnotationIntrospector extends JacksonAnnotationIntrospector
2626
result
2727
}
2828

29-
private def annotatedClassFor(am: AnnotatedMember): AnnotatedClass =
30-
am.getContextClass
31-
3229
private def fieldName(af: AnnotatedField): Option[String] = {
3330
val d = _descriptorFor(af.getDeclaringClass)
3431
d.properties.find(p => p.field.exists(_ == af.getAnnotated)).map(_.name)
@@ -47,7 +44,7 @@ object ScalaAnnotationIntrospector extends JacksonAnnotationIntrospector
4744
}
4845

4946
private def isScalaPackage(pkg: Option[Package]): Boolean =
50-
pkg flatMap { _.getName.split("\\.").headOption } exists { _ == "scala" }
47+
pkg.exists(_.getName.startsWith("scala."))
5148

5249
private def isMaybeScalaBeanType(cls: Class[_]): Boolean =
5350
cls.hasSignature && !isScalaPackage(Option(cls.getPackage))
@@ -76,18 +73,6 @@ object ScalaAnnotationIntrospector extends JacksonAnnotationIntrospector
7673
}
7774
}
7875

79-
private def paramFor(a: Annotated): Option[AnnotatedParameter] = {
80-
a match {
81-
case am: AnnotatedMember =>
82-
propertyFor(a).flatMap(_.param).flatMap { cp =>
83-
annotatedClassFor(am)
84-
.getConstructors.asScala.find(_.getAnnotated == cp.constructor)
85-
.map(_.getParameter(cp.index))
86-
}
87-
case _ => None
88-
}
89-
}
90-
9176
override def findImplicitPropertyName(member: AnnotatedMember): String = {
9277
member match {
9378
case af: AnnotatedField => fieldName(af).orNull
@@ -97,28 +82,21 @@ object ScalaAnnotationIntrospector extends JacksonAnnotationIntrospector
9782
}
9883
}
9984

100-
override def findNameForSerialization(member: Annotated): PropertyName =
101-
paramFor(member).flatMap(p => Option(super.findNameForSerialization(p))).orNull
102-
10385
override def hasCreatorAnnotation(a: Annotated): Boolean = {
104-
if (!isScala(a)) {
105-
return super.hasCreatorAnnotation(a)
106-
}
107-
10886
a match {
10987
case ac: AnnotatedConstructor =>
110-
val d = _descriptorFor(ac.getDeclaringClass)
111-
d.properties.exists(p => p.param.exists(_.constructor == ac.getAnnotated))
88+
isScala(ac) && _descriptorFor(ac.getDeclaringClass).
89+
properties.view.flatMap(_.param).exists(_.constructor == ac.getAnnotated)
11290
case _ => false
11391
}
11492
}
11593

11694
override def findCreatorBinding(a: Annotated): JsonCreator.Mode =
117-
paramFor(a) optMap { _.getAnnotation(classOf[JsonCreator]) } map { _.mode } getOrElse {
118-
if (hasCreatorAnnotation(a)) JsonCreator.Mode.PROPERTIES else null
119-
}
95+
if (isScala(a) && hasCreatorAnnotation(a)) JsonCreator.Mode.PROPERTIES else null
96+
12097
}
12198

12299
trait ScalaAnnotationIntrospectorModule extends JacksonModule {
100+
this += { _.appendAnnotationIntrospector(new ParanamerAnnotationIntrospector()) }
123101
this += { _.appendAnnotationIntrospector(ScalaAnnotationIntrospector) }
124102
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.fasterxml.jackson.module.scala.introspect;
2+
3+
import com.fasterxml.jackson.annotation.JacksonAnnotation;
4+
5+
import java.lang.annotation.ElementType;
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.RetentionPolicy;
8+
import java.lang.annotation.Target;
9+
10+
/**
11+
* A marker annotation with no semantic meaning, use to
12+
* test annotation detection.
13+
*/
14+
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
15+
@Retention(RetentionPolicy.RUNTIME)
16+
@JacksonAnnotation
17+
public @interface JsonScalaTestAnnotation {
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package com.fasterxml.jackson
2+
package module.scala
3+
package introspect
4+
5+
import com.fasterxml.jackson.core.JsonGenerator
6+
import com.fasterxml.jackson.databind
7+
import com.fasterxml.jackson.databind.module.SimpleModule
8+
import com.fasterxml.jackson.databind.ser.ContextualSerializer
9+
import com.fasterxml.jackson.databind.{SerializerProvider, JsonSerializer, BeanDescription, ObjectMapper}
10+
import experimental.ScalaObjectMapper
11+
12+
import org.junit.runner.RunWith
13+
import org.scalatest.{fixture, Matchers}
14+
import org.scalatest.junit.JUnitRunner
15+
import org.scalatest.LoneElement._
16+
17+
import beans.BeanProperty
18+
import collection.JavaConverters._
19+
20+
object ScalaAnnotationIntrospectorTest
21+
{
22+
class Token
23+
class BasicPropertyClass(val param: Int)
24+
class BeanPropertyClass(@BeanProperty val param: Int)
25+
class AnnotatedBasicPropertyClass(@JsonScalaTestAnnotation val param: Token)
26+
class AnnotatedBeanPropertyClass(@JsonScalaTestAnnotation @BeanProperty val param: Token)
27+
}
28+
29+
@RunWith(classOf[JUnitRunner])
30+
class ScalaAnnotationIntrospectorTest extends fixture.FlatSpec with Matchers {
31+
import ScalaAnnotationIntrospectorTest._
32+
33+
type FixtureParam = ObjectMapper with ScalaObjectMapper
34+
35+
override def withFixture(test: OneArgTest) = {
36+
val mapper = new ObjectMapper with ScalaObjectMapper
37+
mapper.registerModule(DefaultScalaModule)
38+
withFixture(test.toNoArgTest(mapper))
39+
}
40+
41+
behavior of "ScalaAnnotationIntrospector"
42+
43+
it should "detect a val property" in { mapper =>
44+
val bean = new BasicPropertyClass(1)
45+
val allProps = getProps(mapper, bean)
46+
allProps.loneElement should have ('name ("param"))
47+
48+
val prop = allProps.asScala.head
49+
prop should have (
50+
'hasField (true),
51+
'hasGetter (true),
52+
'hasConstructorParameter (true)
53+
)
54+
55+
val accessor = prop.getAccessor
56+
accessor shouldNot be (null)
57+
}
58+
59+
it should "detect annotations on a val property" in { mapper =>
60+
mapper.registerModule(new SimpleModule() {
61+
addSerializer(new JsonSerializer[Token] with ContextualSerializer {
62+
override val handledType = classOf[Token]
63+
override def serialize(value: Token, gen: JsonGenerator, serializers: SerializerProvider): Unit =
64+
gen.writeString("")
65+
override def createContextual(prov: SerializerProvider, property: databind.BeanProperty): JsonSerializer[_] = {
66+
property.getAnnotation(classOf[JsonScalaTestAnnotation]) shouldNot be (null)
67+
this
68+
}
69+
})
70+
})
71+
72+
val bean = new AnnotatedBasicPropertyClass(new Token)
73+
mapper.writeValueAsString(bean)
74+
}
75+
76+
it should "detect a bean property" in { mapper =>
77+
val bean = new BeanPropertyClass(1)
78+
val allProps = getProps(mapper, bean)
79+
allProps.loneElement should have ('name ("param"))
80+
81+
val prop = allProps.asScala.head
82+
prop should have (
83+
'hasField (true),
84+
'hasGetter (true),
85+
'hasConstructorParameter (true)
86+
)
87+
88+
val accessor = prop.getAccessor
89+
accessor shouldNot be (null)
90+
}
91+
92+
it should "detect annotations on a bean property" in { mapper =>
93+
mapper.registerModule(new SimpleModule() {
94+
addSerializer(new JsonSerializer[Token] with ContextualSerializer {
95+
override val handledType = classOf[Token]
96+
override def serialize(value: Token, gen: JsonGenerator, serializers: SerializerProvider): Unit =
97+
gen.writeString("")
98+
override def createContextual(prov: SerializerProvider, property: databind.BeanProperty): JsonSerializer[_] = {
99+
property.getAnnotation(classOf[JsonScalaTestAnnotation]) shouldNot be (null)
100+
this
101+
}
102+
})
103+
})
104+
105+
val bean = new AnnotatedBeanPropertyClass(new Token)
106+
val allProps = getProps(mapper, bean)
107+
allProps.loneElement should have ('name ("param"))
108+
109+
val prop = allProps.asScala.head
110+
prop should have (
111+
'hasField (true),
112+
'hasGetter (true),
113+
'hasConstructorParameter (true)
114+
)
115+
val param = prop.getConstructorParameter
116+
param.getAnnotation(classOf[JsonScalaTestAnnotation]) shouldNot be (null)
117+
118+
mapper.writeValueAsString(bean)
119+
}
120+
121+
private def getProps(mapper: ObjectMapper, bean: AnyRef) = {
122+
val config = mapper.getSerializationConfig
123+
val beanDescription: BeanDescription = config.introspect(mapper.constructType(bean.getClass))
124+
beanDescription.findProperties()
125+
}
126+
}

src/test/scala/com/fasterxml/jackson/module/scala/ser/CaseClassSerializerTest.scala

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.fasterxml.jackson.module.scala.ser
22

3+
import com.fasterxml.jackson.annotation.JsonProperty.Access
34
import org.junit.runner.RunWith
45
import org.scalatest.junit.JUnitRunner
56

@@ -56,7 +57,10 @@ class NonCaseWithBeanProperty {
5657

5758
case class InnerJavaEnum(fieldType: Field.Type)
5859

59-
case class PrivateDefaultFields @JsonCreator() (@JsonProperty("firstName") private val firstName: String, @JsonProperty("lastName") lastName: String = "Freeman")
60+
case class PrivateDefaultFields(
61+
@JsonProperty(access = Access.READ_ONLY) private val firstName: String,
62+
@JsonProperty lastName: String = "Freeman"
63+
)
6064

6165
@RunWith(classOf[JUnitRunner])
6266
class CaseClassSerializerTest extends SerializerTest {
@@ -102,7 +106,7 @@ class CaseClassSerializerTest extends SerializerTest {
102106
}
103107

104108
it should "serialize a case class with unicode name properties" in {
105-
serialize(UnicodeNameCaseClass(23, "the name of this")) should equal( """{"winning-id":23,"name":"the name of this"}""")
109+
serialize(UnicodeNameCaseClass(23, "the name of this")) should equal( """{"name":"the name of this","winning-id":23}""")
106110
}
107111

108112
it should "seralize a generic case class" in {
@@ -184,4 +188,4 @@ class CaseClassSerializerTest extends SerializerTest {
184188
val foo = new Foo(java.util.Arrays.asList("foo", "bar"))
185189
serialize(foo) should equal ("""{"strings":["foo","bar"]}""")
186190
}
187-
}
191+
}

0 commit comments

Comments
 (0)