From b4bbe4fd48037d6dcd9a5d32c51bc9aa6fbcb1e8 Mon Sep 17 00:00:00 2001 From: Gian Merlino Date: Thu, 19 Apr 2012 07:50:20 -0700 Subject: [PATCH 1/3] Failing test case --- .../jerkson/tests/CaseClassSupportSpec.scala | 15 +++++++++++++++ .../jerkson/tests/ExampleCaseClasses.scala | 2 ++ 2 files changed, 17 insertions(+) diff --git a/src/test/scala/com/codahale/jerkson/tests/CaseClassSupportSpec.scala b/src/test/scala/com/codahale/jerkson/tests/CaseClassSupportSpec.scala index 7a562f5..47a3eed 100644 --- a/src/test/scala/com/codahale/jerkson/tests/CaseClassSupportSpec.scala +++ b/src/test/scala/com/codahale/jerkson/tests/CaseClassSupportSpec.scala @@ -236,4 +236,19 @@ class CaseClassSupportSpec extends Spec { )) } } + + class `A parameterized case class` { + @Test def `is parsable from a JSON object` = { + val c = parse[CaseClassWithParameter[Int]]("""{"id":"1","list":[2,3]}""") + + c.id.must(be(1)) + c.list.must(be(List(2,3))) + } + + @Test def `generates a JSON object` = { + generate(CaseClassWithParameter[Int](1, List(2, 3))).must(be( + """{"id":1,"list":[2,3]}""" + )) + } + } } diff --git a/src/test/scala/com/codahale/jerkson/tests/ExampleCaseClasses.scala b/src/test/scala/com/codahale/jerkson/tests/ExampleCaseClasses.scala index c3b666f..bc33236 100644 --- a/src/test/scala/com/codahale/jerkson/tests/ExampleCaseClasses.scala +++ b/src/test/scala/com/codahale/jerkson/tests/ExampleCaseClasses.scala @@ -72,3 +72,5 @@ case class CaseClassWithTwoConstructors(id: Long, name: String) { case class CaseClassWithSnakeCase(oneThing: String, twoThing: String) case class CaseClassWithArrays(one: String, two: Array[String], three: Array[Int]) + +case class CaseClassWithParameter[T](id: Long, list: List[T]) From be43b0bc54e77537e8703dfa1c02a5f50f745db2 Mon Sep 17 00:00:00 2001 From: Gian Merlino Date: Thu, 19 Apr 2012 15:19:03 -0700 Subject: [PATCH 2/3] Pass along contained types (according to the original JavaType) to typeRef2JavaType so they can serve as hints for generic types --- .../jerkson/deser/CaseClassDeserializer.scala | 2 +- .../jerkson/util/CaseClassSigParser.scala | 27 ++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main/scala/com/codahale/jerkson/deser/CaseClassDeserializer.scala b/src/main/scala/com/codahale/jerkson/deser/CaseClassDeserializer.scala index a383419..fa93e04 100644 --- a/src/main/scala/com/codahale/jerkson/deser/CaseClassDeserializer.scala +++ b/src/main/scala/com/codahale/jerkson/deser/CaseClassDeserializer.scala @@ -17,7 +17,7 @@ class CaseClassDeserializer(config: DeserializationConfig, provider: DeserializerProvider, classLoader: ClassLoader) extends JsonDeserializer[Object] { private val isSnakeCase = javaType.getRawClass.isAnnotationPresent(classOf[JsonSnakeCase]) - private val params = CaseClassSigParser.parse(javaType.getRawClass, config.getTypeFactory, classLoader).map { + private val params = CaseClassSigParser.parse(javaType, config.getTypeFactory, classLoader).map { case (name, jt) => (if (isSnakeCase) snakeCase(name) else name, jt) }.toArray private val paramTypes = params.map { _._2.getRawClass }.toList diff --git a/src/main/scala/com/codahale/jerkson/util/CaseClassSigParser.scala b/src/main/scala/com/codahale/jerkson/util/CaseClassSigParser.scala index 3895ea8..802142b 100644 --- a/src/main/scala/com/codahale/jerkson/util/CaseClassSigParser.scala +++ b/src/main/scala/com/codahale/jerkson/util/CaseClassSigParser.scala @@ -79,20 +79,23 @@ object CaseClassSigParser { } } - def parse[A](clazz: Class[A], factory: TypeFactory, classLoader: ClassLoader) = { + def parse[A](javaType: JavaType, factory: TypeFactory, classLoader: ClassLoader) = { + val clazz = javaType.getRawClass + val containedTypes: Map[String, JavaType] = ((0 until javaType.containedTypeCount).map { i => javaType.containedTypeName(i) -> javaType.containedType(i) }).toMap + findSym(clazz, classLoader).children.filter(c => c.isCaseAccessor && !c.isPrivate) .flatMap { ms => ms.asInstanceOf[MethodSymbol].infoType match { - case NullaryMethodType(t: TypeRefType) => ms.name -> typeRef2JavaType(t, factory, classLoader) :: Nil + case NullaryMethodType(t: TypeRefType) => ms.name -> typeRef2JavaType(t, factory, classLoader, containedTypes) :: Nil case _ => Nil } } } - protected def typeRef2JavaType(ref: TypeRefType, factory: TypeFactory, classLoader: ClassLoader): JavaType = { + protected def typeRef2JavaType(ref: TypeRefType, factory: TypeFactory, classLoader: ClassLoader, containedTypes: Map[String, JavaType]): JavaType = { try { if (ref.symbol.path == "scala.Array") { - val elementType = typeRef2JavaType(ref.typeArgs.head.asInstanceOf[TypeRefType], factory, classLoader) + val elementType = typeRef2JavaType(ref.typeArgs.head.asInstanceOf[TypeRefType], factory, classLoader, containedTypes) val realElementType = elementType.getRawClass.getName match { case "java.lang.Boolean" => classOf[Boolean] case "java.lang.Byte" => classOf[Byte] @@ -108,12 +111,16 @@ object CaseClassSigParser { val array = java.lang.reflect.Array.newInstance(realElementType, 0) factory.constructType(array.getClass) } else { - val klass = loadClass(ref.symbol.path, classLoader) - factory.constructParametricType( - klass, ref.typeArgs.map { - t => typeRef2JavaType(t.asInstanceOf[TypeRefType], factory, classLoader) - }: _* - ) + if(containedTypes.contains(ref.symbol.path)) { + containedTypes(ref.symbol.path) + } else { + val klass = loadClass(ref.symbol.path, classLoader) + factory.constructParametricType( + klass, ref.typeArgs.map { + t => typeRef2JavaType(t.asInstanceOf[TypeRefType], factory, classLoader, containedTypes) + }: _* + ) + } } } catch { case e: Throwable => { From 3334c79e9e781211dce85005f243bce95d910ceb Mon Sep 17 00:00:00 2001 From: Gian Merlino Date: Thu, 19 Apr 2012 15:41:47 -0700 Subject: [PATCH 3/3] Add test for multi-level parameterized case class --- .../jerkson/tests/CaseClassSupportSpec.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/scala/com/codahale/jerkson/tests/CaseClassSupportSpec.scala b/src/test/scala/com/codahale/jerkson/tests/CaseClassSupportSpec.scala index 47a3eed..fdf5a55 100644 --- a/src/test/scala/com/codahale/jerkson/tests/CaseClassSupportSpec.scala +++ b/src/test/scala/com/codahale/jerkson/tests/CaseClassSupportSpec.scala @@ -251,4 +251,19 @@ class CaseClassSupportSpec extends Spec { )) } } + + class `A multi-level parameterized case class` { + @Test def `is parsable from a JSON object` = { + val c = parse[CaseClassWithParameter[List[CaseClassWithParameter[Int]]]]("""{"id":"1","list":[[{"id":"2","list":[3]}]]}""") + + c.id.must(be(1)) + c.list.must(be(List(List(CaseClassWithParameter[Int](2,List(3)))))) + } + + @Test def `generates a JSON object` = { + generate(CaseClassWithParameter[List[CaseClassWithParameter[Int]]](1,List(List(CaseClassWithParameter[Int](2,List(3)))))).must(be( + """{"id":1,"list":[[{"id":2,"list":[3]}]]}""" + )) + } + } }