Skip to content

Commit 7ea191e

Browse files
gaeljwpjfanning
authored andcommitted
feat: 🚀 Support parameterized types in ClassTagExtensions (#518)
Thanks very much @gaeljw - I'll merge this and see what happens with build - it was probably just some travis issue that caused the build failure
1 parent 66b69dc commit 7ea191e

File tree

3 files changed

+200
-61
lines changed

3 files changed

+200
-61
lines changed

src/main/scala-2.+/com/fasterxml/jackson/module/scala/ScalaObjectMapper.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object ScalaObjectMapper {
1414
extends JsonMapper(mapper.rebuild()) with ScalaObjectMapper
1515
}
1616

17-
@deprecated("ScalaObjectMapper is deprecated because Manifests are not supported in Scala3", "2.12.1")
17+
@deprecated("ScalaObjectMapper is deprecated because Manifests are not supported in Scala3, you might want to use ClassTagExtensions as a replacement", "2.12.1")
1818
trait ScalaObjectMapper {
1919
self: ObjectMapper =>
2020

src/main/scala/com/fasterxml/jackson/module/scala/ClassTagExtensions.scala

+173-38
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.fasterxml.jackson.module.scala
22

33
import com.fasterxml.jackson.core.{JsonParser, TreeNode}
44
import com.fasterxml.jackson.databind._
5+
import com.fasterxml.jackson.databind.`type`.TypeFactory
56
import com.fasterxml.jackson.databind.json.JsonMapper
67

78
import java.io.{File, InputStream, Reader}
@@ -20,11 +21,8 @@ object ClassTagExtensions {
2021
}
2122

2223
/**
23-
* Experimental ClassTag equivalent of ScalaObjectMapper. This does not do a good job with
24-
* reference types that wrap primitives, eg Option[Int], Seq[Boolean].
25-
*
26-
* This is because ClassTags only provide access to the Java class and information
27-
* about the wrapped types is lost due to type erasure.
24+
* Experimental ClassTag equivalent of ScalaObjectMapper.
25+
* This only works with non parameterized types or parameterized types up to 5 type parameters.
2826
*/
2927
trait ClassTagExtensions {
3028
self: ObjectMapper =>
@@ -35,6 +33,15 @@ trait ClassTagExtensions {
3533
**********************************************************
3634
*/
3735

36+
/**
37+
* Convenience method for constructing [[com.fasterxml.jackson.databind.JavaType]] out of given
38+
* type (typically <code>java.lang.Class</code>), but without explicit
39+
* context.
40+
*/
41+
def constructType[T: JavaTypeable]: JavaType = {
42+
implicitly[JavaTypeable[T]].asJavaType(getTypeFactory)
43+
}
44+
3845
/*
3946
**********************************************************
4047
* Public API (from ObjectCodec): deserialization
@@ -50,8 +57,8 @@ trait ClassTagExtensions {
5057
* and specifically needs to be used if the root type is a
5158
* parameterized (generic) container type.
5259
*/
53-
def readValue[T: ClassTag](jp: JsonParser): T = {
54-
readValue(jp, classFor[T])
60+
def readValue[T: JavaTypeable](jp: JsonParser): T = {
61+
readValue(jp, constructType[T])
5562
}
5663

5764
/**
@@ -66,8 +73,8 @@ trait ClassTagExtensions {
6673
* <p>
6774
* Note that [[com.fasterxml.jackson.databind.ObjectReader]] has more complete set of variants.
6875
*/
69-
def readValues[T: ClassTag](jp: JsonParser): MappingIterator[T] = {
70-
readValues(jp, classFor[T])
76+
def readValues[T: JavaTypeable](jp: JsonParser): MappingIterator[T] = {
77+
readValues(jp, constructType[T])
7178
}
7279

7380
/*
@@ -95,64 +102,64 @@ trait ClassTagExtensions {
95102
* convenience methods
96103
**********************************************************
97104
*/
98-
def readValue[T: ClassTag](src: File): T = {
99-
readValue(src, classFor[T])
105+
def readValue[T: JavaTypeable](src: File): T = {
106+
readValue(src, constructType[T])
100107
}
101108

102-
def readValue[T: ClassTag](src: URL): T = {
103-
readValue(src, classFor[T])
109+
def readValue[T: JavaTypeable](src: URL): T = {
110+
readValue(src, constructType[T])
104111
}
105112

106-
def readValue[T: ClassTag](content: String): T = {
107-
readValue(content, classFor[T])
113+
def readValue[T: JavaTypeable](content: String): T = {
114+
readValue(content, constructType[T])
108115
}
109116

110-
def readValue[T: ClassTag](src: Reader): T = {
111-
readValue(src, classFor[T])
117+
def readValue[T: JavaTypeable](src: Reader): T = {
118+
readValue(src, constructType[T])
112119
}
113120

114-
def readValue[T: ClassTag](src: InputStream): T = {
115-
readValue(src, classFor[T])
121+
def readValue[T: JavaTypeable](src: InputStream): T = {
122+
readValue(src, constructType[T])
116123
}
117124

118-
def readValue[T: ClassTag](src: Array[Byte]): T = {
119-
readValue(src, classFor[T])
125+
def readValue[T: JavaTypeable](src: Array[Byte]): T = {
126+
readValue(src, constructType[T])
120127
}
121128

122-
def readValue[T: ClassTag](src: Array[Byte], offset: Int, len: Int): T = {
123-
readValue(src, offset, len, classFor[T])
129+
def readValue[T: JavaTypeable](src: Array[Byte], offset: Int, len: Int): T = {
130+
readValue(src, offset, len, constructType[T])
124131
}
125132

126-
def updateValue[T: ClassTag](valueToUpdate: T, src: File): T = {
133+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: File): T = {
127134
objectReaderFor(valueToUpdate).readValue(src)
128135
}
129136

130-
def updateValue[T: ClassTag](valueToUpdate: T, src: URL): T = {
137+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: URL): T = {
131138
objectReaderFor(valueToUpdate).readValue(src)
132139
}
133140

134-
def updateValue[T: ClassTag](valueToUpdate: T, content: String): T = {
141+
def updateValue[T: JavaTypeable](valueToUpdate: T, content: String): T = {
135142
objectReaderFor(valueToUpdate).readValue(content)
136143
}
137144

138-
def updateValue[T: ClassTag](valueToUpdate: T, src: Reader): T = {
145+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: Reader): T = {
139146
objectReaderFor(valueToUpdate).readValue(src)
140147
}
141148

142-
def updateValue[T: ClassTag](valueToUpdate: T, src: InputStream): T = {
149+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: InputStream): T = {
143150
objectReaderFor(valueToUpdate).readValue(src)
144151
}
145152

146-
def updateValue[T: ClassTag](valueToUpdate: T, src: Array[Byte]): T = {
153+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: Array[Byte]): T = {
147154
objectReaderFor(valueToUpdate).readValue(src)
148155
}
149156

150-
def updateValue[T: ClassTag](valueToUpdate: T, src: Array[Byte], offset: Int, len: Int): T = {
157+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: Array[Byte], offset: Int, len: Int): T = {
151158
objectReaderFor(valueToUpdate).readValue(src, offset, len)
152159
}
153160

154-
private def objectReaderFor[T: ClassTag](valueToUpdate: T): ObjectReader = {
155-
readerForUpdating(valueToUpdate).forType(classFor[T])
161+
private def objectReaderFor[T: JavaTypeable](valueToUpdate: T): ObjectReader = {
162+
readerForUpdating(valueToUpdate).forType(constructType[T])
156163
}
157164

158165
/*
@@ -181,8 +188,8 @@ trait ClassTagExtensions {
181188
*
182189
* @since 2.5
183190
*/
184-
def writerFor[T: ClassTag]: ObjectWriter = {
185-
writerFor(classFor[T])
191+
def writerFor[T: JavaTypeable]: ObjectWriter = {
192+
writerFor(constructType[T])
186193
}
187194

188195
/*
@@ -196,8 +203,8 @@ trait ClassTagExtensions {
196203
* Factory method for constructing [[com.fasterxml.jackson.databind.ObjectReader]] that will
197204
* read or update instances of specified type
198205
*/
199-
def readerFor[T: ClassTag]: ObjectReader = {
200-
readerFor(classFor[T])
206+
def readerFor[T: JavaTypeable]: ObjectReader = {
207+
readerFor(constructType[T])
201208
}
202209

203210
/**
@@ -226,11 +233,139 @@ trait ClassTagExtensions {
226233
* if so, root cause will contain underlying checked exception data binding
227234
* functionality threw
228235
*/
229-
def convertValue[T: ClassTag](fromValue: Any): T = {
230-
convertValue(fromValue, classFor[T])
236+
def convertValue[T: JavaTypeable](fromValue: Any): T = {
237+
convertValue(fromValue, constructType[T])
231238
}
232239

233240
private def classFor[T: ClassTag]: Class[T] = {
234241
implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]]
235242
}
236243
}
244+
245+
trait JavaTypeable[T] {
246+
def asJavaType(typeFactory: TypeFactory): JavaType
247+
}
248+
249+
object JavaTypeable {
250+
251+
implicit val anyJavaTypeable: JavaTypeable[Any] = {
252+
new JavaTypeable[Any] {
253+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
254+
val typeArgs: Array[JavaType] = Array()
255+
typeFactory.constructParametricType(classOf[Object], typeArgs: _*)
256+
}
257+
}
258+
}
259+
260+
implicit def optionJavaTypeable[T : JavaTypeable]: JavaTypeable[Option[T]] = {
261+
new JavaTypeable[Option[T]] {
262+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
263+
val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory)
264+
typeFactory.constructReferenceType(classOf[Option[_]], typeArg0)
265+
}
266+
}
267+
}
268+
269+
implicit def arrayJavaTypeable[T : JavaTypeable]: JavaTypeable[Array[T]] = {
270+
new JavaTypeable[Array[T]] {
271+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
272+
val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory)
273+
typeFactory.constructArrayType(typeArg0)
274+
}
275+
}
276+
}
277+
278+
implicit def mapJavaTypeable[M[_,_] <: Map[_,_], K : JavaTypeable, V: JavaTypeable](implicit ct: ClassTag[M[K,V]]): JavaTypeable[M[K, V]] = {
279+
new JavaTypeable[M[K, V]] {
280+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
281+
val typeArg0 = implicitly[JavaTypeable[K]].asJavaType(typeFactory)
282+
val typeArg1 = implicitly[JavaTypeable[V]].asJavaType(typeFactory)
283+
typeFactory.constructMapLikeType(ct.runtimeClass, typeArg0, typeArg1)
284+
}
285+
}
286+
}
287+
288+
implicit def collectionJavaTypeable[I[_] <: Iterable[_], T : JavaTypeable](implicit ct: ClassTag[I[T]]): JavaTypeable[I[T]] = {
289+
new JavaTypeable[I[T]] {
290+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
291+
val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory)
292+
typeFactory.constructCollectionLikeType(ct.runtimeClass, typeArg0)
293+
}
294+
}
295+
}
296+
297+
implicit def gen5JavaTypeable[T[_, _, _, _, _], A: JavaTypeable, B: JavaTypeable, C: JavaTypeable, D: JavaTypeable, E: JavaTypeable](implicit ct: ClassTag[T[A, B, C, D, E]]): JavaTypeable[T[A, B, C, D, E]] = {
298+
new JavaTypeable[T[A, B, C, D, E]] {
299+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
300+
val typeArgs: Array[JavaType] = Array(
301+
implicitly[JavaTypeable[A]].asJavaType(typeFactory),
302+
implicitly[JavaTypeable[B]].asJavaType(typeFactory),
303+
implicitly[JavaTypeable[C]].asJavaType(typeFactory),
304+
implicitly[JavaTypeable[D]].asJavaType(typeFactory),
305+
implicitly[JavaTypeable[E]].asJavaType(typeFactory)
306+
)
307+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
308+
}
309+
}
310+
}
311+
312+
implicit def gen4JavaTypeable[T[_, _, _, _], A: JavaTypeable, B: JavaTypeable, C: JavaTypeable, D: JavaTypeable](implicit ct: ClassTag[T[A, B, C, D]]): JavaTypeable[T[A, B, C, D]] = {
313+
new JavaTypeable[T[A, B, C, D]] {
314+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
315+
val typeArgs: Array[JavaType] = Array(
316+
implicitly[JavaTypeable[A]].asJavaType(typeFactory),
317+
implicitly[JavaTypeable[B]].asJavaType(typeFactory),
318+
implicitly[JavaTypeable[C]].asJavaType(typeFactory),
319+
implicitly[JavaTypeable[D]].asJavaType(typeFactory)
320+
)
321+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
322+
}
323+
}
324+
}
325+
326+
implicit def gen3JavaTypeable[T[_, _, _], A: JavaTypeable, B: JavaTypeable, C: JavaTypeable](implicit ct: ClassTag[T[A, B, C]]): JavaTypeable[T[A, B, C]] = {
327+
new JavaTypeable[T[A, B, C]] {
328+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
329+
val typeArgs: Array[JavaType] = Array(
330+
implicitly[JavaTypeable[A]].asJavaType(typeFactory),
331+
implicitly[JavaTypeable[B]].asJavaType(typeFactory),
332+
implicitly[JavaTypeable[C]].asJavaType(typeFactory)
333+
)
334+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
335+
}
336+
}
337+
}
338+
339+
implicit def gen2JavaTypeable[T[_, _], A: JavaTypeable, B: JavaTypeable](implicit ct: ClassTag[T[A, B]]): JavaTypeable[T[A, B]] = {
340+
new JavaTypeable[T[A, B]] {
341+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
342+
val typeArgs: Array[JavaType] = Array(
343+
implicitly[JavaTypeable[A]].asJavaType(typeFactory),
344+
implicitly[JavaTypeable[B]].asJavaType(typeFactory)
345+
)
346+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
347+
}
348+
}
349+
}
350+
351+
implicit def gen1JavaTypeable[T[_], A: JavaTypeable](implicit ct: ClassTag[T[A]]): JavaTypeable[T[A]] = {
352+
new JavaTypeable[T[A]] {
353+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
354+
val typeArgs: Array[JavaType] = Array(
355+
implicitly[JavaTypeable[A]].asJavaType(typeFactory)
356+
)
357+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
358+
}
359+
}
360+
}
361+
362+
implicit def gen0JavaTypeable[T](implicit ct: ClassTag[T]): JavaTypeable[T] = {
363+
new JavaTypeable[T] {
364+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
365+
val typeArgs: Array[JavaType] = Array()
366+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
367+
}
368+
}
369+
}
370+
371+
}

0 commit comments

Comments
 (0)