Skip to content

Commit 2dab7c3

Browse files
authored
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 03f879a commit 2dab7c3

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
@@ -15,7 +15,7 @@ object ScalaObjectMapper {
1515
extends JsonMapper(mapper.rebuild().build()) with ScalaObjectMapper
1616
}
1717

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

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}
@@ -15,11 +16,8 @@ object ClassTagExtensions {
1516
}
1617

1718
/**
18-
* Experimental ClassTag equivalent of ScalaObjectMapper. This does not do a good job with
19-
* reference types that wrap primitives, eg Option[Int], Seq[Boolean].
20-
*
21-
* This is because ClassTags only provide access to the Java class and information
22-
* about the wrapped types is lost due to type erasure.
19+
* Experimental ClassTag equivalent of ScalaObjectMapper.
20+
* This only works with non parameterized types or parameterized types up to 5 type parameters.
2321
*/
2422
trait ClassTagExtensions {
2523
self: ObjectMapper =>
@@ -56,6 +54,15 @@ trait ClassTagExtensions {
5654
**********************************************************
5755
*/
5856

57+
/**
58+
* Convenience method for constructing [[com.fasterxml.jackson.databind.JavaType]] out of given
59+
* type (typically <code>java.lang.Class</code>), but without explicit
60+
* context.
61+
*/
62+
def constructType[T: JavaTypeable]: JavaType = {
63+
implicitly[JavaTypeable[T]].asJavaType(getTypeFactory)
64+
}
65+
5966
/*
6067
**********************************************************
6168
* Public API (from ObjectCodec): deserialization
@@ -71,8 +78,8 @@ trait ClassTagExtensions {
7178
* and specifically needs to be used if the root type is a
7279
* parameterized (generic) container type.
7380
*/
74-
def readValue[T: ClassTag](jp: JsonParser): T = {
75-
readValue(jp, classFor[T])
81+
def readValue[T: JavaTypeable](jp: JsonParser): T = {
82+
readValue(jp, constructType[T])
7683
}
7784

7885
/**
@@ -87,8 +94,8 @@ trait ClassTagExtensions {
8794
* <p>
8895
* Note that [[com.fasterxml.jackson.databind.ObjectReader]] has more complete set of variants.
8996
*/
90-
def readValues[T: ClassTag](jp: JsonParser): MappingIterator[T] = {
91-
readValues(jp, classFor[T])
97+
def readValues[T: JavaTypeable](jp: JsonParser): MappingIterator[T] = {
98+
readValues(jp, constructType[T])
9299
}
93100

94101
/*
@@ -116,64 +123,64 @@ trait ClassTagExtensions {
116123
* convenience methods
117124
**********************************************************
118125
*/
119-
def readValue[T: ClassTag](src: File): T = {
120-
readValue(src, classFor[T])
126+
def readValue[T: JavaTypeable](src: File): T = {
127+
readValue(src, constructType[T])
121128
}
122129

123-
def readValue[T: ClassTag](src: URL): T = {
124-
readValue(src, classFor[T])
130+
def readValue[T: JavaTypeable](src: URL): T = {
131+
readValue(src, constructType[T])
125132
}
126133

127-
def readValue[T: ClassTag](content: String): T = {
128-
readValue(content, classFor[T])
134+
def readValue[T: JavaTypeable](content: String): T = {
135+
readValue(content, constructType[T])
129136
}
130137

131-
def readValue[T: ClassTag](src: Reader): T = {
132-
readValue(src, classFor[T])
138+
def readValue[T: JavaTypeable](src: Reader): T = {
139+
readValue(src, constructType[T])
133140
}
134141

135-
def readValue[T: ClassTag](src: InputStream): T = {
136-
readValue(src, classFor[T])
142+
def readValue[T: JavaTypeable](src: InputStream): T = {
143+
readValue(src, constructType[T])
137144
}
138145

139-
def readValue[T: ClassTag](src: Array[Byte]): T = {
140-
readValue(src, classFor[T])
146+
def readValue[T: JavaTypeable](src: Array[Byte]): T = {
147+
readValue(src, constructType[T])
141148
}
142149

143-
def readValue[T: ClassTag](src: Array[Byte], offset: Int, len: Int): T = {
144-
readValue(src, offset, len, classFor[T])
150+
def readValue[T: JavaTypeable](src: Array[Byte], offset: Int, len: Int): T = {
151+
readValue(src, offset, len, constructType[T])
145152
}
146153

147-
def updateValue[T: ClassTag](valueToUpdate: T, src: File): T = {
154+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: File): T = {
148155
objectReaderFor(valueToUpdate).readValue(src)
149156
}
150157

151-
def updateValue[T: ClassTag](valueToUpdate: T, src: URL): T = {
158+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: URL): T = {
152159
objectReaderFor(valueToUpdate).readValue(src)
153160
}
154161

155-
def updateValue[T: ClassTag](valueToUpdate: T, content: String): T = {
162+
def updateValue[T: JavaTypeable](valueToUpdate: T, content: String): T = {
156163
objectReaderFor(valueToUpdate).readValue(content)
157164
}
158165

159-
def updateValue[T: ClassTag](valueToUpdate: T, src: Reader): T = {
166+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: Reader): T = {
160167
objectReaderFor(valueToUpdate).readValue(src)
161168
}
162169

163-
def updateValue[T: ClassTag](valueToUpdate: T, src: InputStream): T = {
170+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: InputStream): T = {
164171
objectReaderFor(valueToUpdate).readValue(src)
165172
}
166173

167-
def updateValue[T: ClassTag](valueToUpdate: T, src: Array[Byte]): T = {
174+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: Array[Byte]): T = {
168175
objectReaderFor(valueToUpdate).readValue(src)
169176
}
170177

171-
def updateValue[T: ClassTag](valueToUpdate: T, src: Array[Byte], offset: Int, len: Int): T = {
178+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: Array[Byte], offset: Int, len: Int): T = {
172179
objectReaderFor(valueToUpdate).readValue(src, offset, len)
173180
}
174181

175-
private def objectReaderFor[T: ClassTag](valueToUpdate: T): ObjectReader = {
176-
readerForUpdating(valueToUpdate).forType(classFor[T])
182+
private def objectReaderFor[T: JavaTypeable](valueToUpdate: T): ObjectReader = {
183+
readerForUpdating(valueToUpdate).forType(constructType[T])
177184
}
178185

179186
/*
@@ -202,8 +209,8 @@ trait ClassTagExtensions {
202209
*
203210
* @since 2.5
204211
*/
205-
def writerFor[T: ClassTag]: ObjectWriter = {
206-
writerFor(classFor[T])
212+
def writerFor[T: JavaTypeable]: ObjectWriter = {
213+
writerFor(constructType[T])
207214
}
208215

209216
/*
@@ -217,8 +224,8 @@ trait ClassTagExtensions {
217224
* Factory method for constructing [[com.fasterxml.jackson.databind.ObjectReader]] that will
218225
* read or update instances of specified type
219226
*/
220-
def readerFor[T: ClassTag]: ObjectReader = {
221-
readerFor(classFor[T])
227+
def readerFor[T: JavaTypeable]: ObjectReader = {
228+
readerFor(constructType[T])
222229
}
223230

224231
/**
@@ -247,11 +254,139 @@ trait ClassTagExtensions {
247254
* if so, root cause will contain underlying checked exception data binding
248255
* functionality threw
249256
*/
250-
def convertValue[T: ClassTag](fromValue: Any): T = {
251-
convertValue(fromValue, classFor[T])
257+
def convertValue[T: JavaTypeable](fromValue: Any): T = {
258+
convertValue(fromValue, constructType[T])
252259
}
253260

254261
private def classFor[T: ClassTag]: Class[T] = {
255262
implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]]
256263
}
257264
}
265+
266+
trait JavaTypeable[T] {
267+
def asJavaType(typeFactory: TypeFactory): JavaType
268+
}
269+
270+
object JavaTypeable {
271+
272+
implicit val anyJavaTypeable: JavaTypeable[Any] = {
273+
new JavaTypeable[Any] {
274+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
275+
val typeArgs: Array[JavaType] = Array()
276+
typeFactory.constructParametricType(classOf[Object], typeArgs: _*)
277+
}
278+
}
279+
}
280+
281+
implicit def optionJavaTypeable[T : JavaTypeable]: JavaTypeable[Option[T]] = {
282+
new JavaTypeable[Option[T]] {
283+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
284+
val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory)
285+
typeFactory.constructReferenceType(classOf[Option[_]], typeArg0)
286+
}
287+
}
288+
}
289+
290+
implicit def arrayJavaTypeable[T : JavaTypeable]: JavaTypeable[Array[T]] = {
291+
new JavaTypeable[Array[T]] {
292+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
293+
val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory)
294+
typeFactory.constructArrayType(typeArg0)
295+
}
296+
}
297+
}
298+
299+
implicit def mapJavaTypeable[M[_,_] <: Map[_,_], K : JavaTypeable, V: JavaTypeable](implicit ct: ClassTag[M[K,V]]): JavaTypeable[M[K, V]] = {
300+
new JavaTypeable[M[K, V]] {
301+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
302+
val typeArg0 = implicitly[JavaTypeable[K]].asJavaType(typeFactory)
303+
val typeArg1 = implicitly[JavaTypeable[V]].asJavaType(typeFactory)
304+
typeFactory.constructMapLikeType(ct.runtimeClass, typeArg0, typeArg1)
305+
}
306+
}
307+
}
308+
309+
implicit def collectionJavaTypeable[I[_] <: Iterable[_], T : JavaTypeable](implicit ct: ClassTag[I[T]]): JavaTypeable[I[T]] = {
310+
new JavaTypeable[I[T]] {
311+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
312+
val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory)
313+
typeFactory.constructCollectionLikeType(ct.runtimeClass, typeArg0)
314+
}
315+
}
316+
}
317+
318+
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]] = {
319+
new JavaTypeable[T[A, B, C, D, E]] {
320+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
321+
val typeArgs: Array[JavaType] = Array(
322+
implicitly[JavaTypeable[A]].asJavaType(typeFactory),
323+
implicitly[JavaTypeable[B]].asJavaType(typeFactory),
324+
implicitly[JavaTypeable[C]].asJavaType(typeFactory),
325+
implicitly[JavaTypeable[D]].asJavaType(typeFactory),
326+
implicitly[JavaTypeable[E]].asJavaType(typeFactory)
327+
)
328+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
329+
}
330+
}
331+
}
332+
333+
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]] = {
334+
new JavaTypeable[T[A, B, C, D]] {
335+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
336+
val typeArgs: Array[JavaType] = Array(
337+
implicitly[JavaTypeable[A]].asJavaType(typeFactory),
338+
implicitly[JavaTypeable[B]].asJavaType(typeFactory),
339+
implicitly[JavaTypeable[C]].asJavaType(typeFactory),
340+
implicitly[JavaTypeable[D]].asJavaType(typeFactory)
341+
)
342+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
343+
}
344+
}
345+
}
346+
347+
implicit def gen3JavaTypeable[T[_, _, _], A: JavaTypeable, B: JavaTypeable, C: JavaTypeable](implicit ct: ClassTag[T[A, B, C]]): JavaTypeable[T[A, B, C]] = {
348+
new JavaTypeable[T[A, B, C]] {
349+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
350+
val typeArgs: Array[JavaType] = Array(
351+
implicitly[JavaTypeable[A]].asJavaType(typeFactory),
352+
implicitly[JavaTypeable[B]].asJavaType(typeFactory),
353+
implicitly[JavaTypeable[C]].asJavaType(typeFactory)
354+
)
355+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
356+
}
357+
}
358+
}
359+
360+
implicit def gen2JavaTypeable[T[_, _], A: JavaTypeable, B: JavaTypeable](implicit ct: ClassTag[T[A, B]]): JavaTypeable[T[A, B]] = {
361+
new JavaTypeable[T[A, B]] {
362+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
363+
val typeArgs: Array[JavaType] = Array(
364+
implicitly[JavaTypeable[A]].asJavaType(typeFactory),
365+
implicitly[JavaTypeable[B]].asJavaType(typeFactory)
366+
)
367+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
368+
}
369+
}
370+
}
371+
372+
implicit def gen1JavaTypeable[T[_], A: JavaTypeable](implicit ct: ClassTag[T[A]]): JavaTypeable[T[A]] = {
373+
new JavaTypeable[T[A]] {
374+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
375+
val typeArgs: Array[JavaType] = Array(
376+
implicitly[JavaTypeable[A]].asJavaType(typeFactory)
377+
)
378+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
379+
}
380+
}
381+
}
382+
383+
implicit def gen0JavaTypeable[T](implicit ct: ClassTag[T]): JavaTypeable[T] = {
384+
new JavaTypeable[T] {
385+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
386+
val typeArgs: Array[JavaType] = Array()
387+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
388+
}
389+
}
390+
}
391+
392+
}

0 commit comments

Comments
 (0)