1
1
/*
2
- * Copyright 2002-2023 the original author or authors.
2
+ * Copyright 2002-2024 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
17
17
package org .springframework .http .converter ;
18
18
19
19
import java .io .IOException ;
20
+ import java .lang .reflect .Method ;
20
21
import java .lang .reflect .Type ;
21
22
import java .util .HashSet ;
22
23
import java .util .Map ;
23
24
import java .util .Set ;
24
25
26
+ import kotlin .reflect .KFunction ;
27
+ import kotlin .reflect .KType ;
28
+ import kotlin .reflect .full .KCallables ;
29
+ import kotlin .reflect .jvm .ReflectJvmMapping ;
25
30
import kotlinx .serialization .KSerializer ;
26
31
import kotlinx .serialization .SerialFormat ;
27
32
import kotlinx .serialization .SerializersKt ;
28
33
import kotlinx .serialization .descriptors .PolymorphicKind ;
29
34
import kotlinx .serialization .descriptors .SerialDescriptor ;
30
35
31
- import org .springframework .core .GenericTypeResolver ;
36
+ import org .springframework .core .KotlinDetector ;
37
+ import org .springframework .core .MethodParameter ;
38
+ import org .springframework .core .ResolvableType ;
32
39
import org .springframework .http .HttpInputMessage ;
33
40
import org .springframework .http .HttpOutputMessage ;
34
41
import org .springframework .http .MediaType ;
35
42
import org .springframework .lang .Nullable ;
43
+ import org .springframework .util .Assert ;
36
44
import org .springframework .util .ConcurrentReferenceHashMap ;
37
45
38
46
48
56
* @since 6.0
49
57
* @param <T> the type of {@link SerialFormat}
50
58
*/
51
- public abstract class AbstractKotlinSerializationHttpMessageConverter <T extends SerialFormat > extends AbstractGenericHttpMessageConverter <Object > {
59
+ public abstract class AbstractKotlinSerializationHttpMessageConverter <T extends SerialFormat > extends AbstractSmartHttpMessageConverter <Object > {
52
60
53
- private final Map <Type , KSerializer <Object >> serializerCache = new ConcurrentReferenceHashMap <>();
61
+ private final Map <KType , KSerializer <Object >> kTypeSerializerCache = new ConcurrentReferenceHashMap <>();
62
+
63
+ private final Map <Type , KSerializer <Object >> typeSerializerCache = new ConcurrentReferenceHashMap <>();
54
64
55
65
private final T format ;
56
66
@@ -66,15 +76,14 @@ protected AbstractKotlinSerializationHttpMessageConverter(T format, MediaType...
66
76
this .format = format ;
67
77
}
68
78
69
-
70
79
@ Override
71
80
protected boolean supports (Class <?> clazz ) {
72
- return serializer (clazz ) != null ;
81
+ return serializer (ResolvableType . forClass ( clazz ) ) != null ;
73
82
}
74
83
75
84
@ Override
76
- public boolean canRead (Type type , @ Nullable Class <?> contextClass , @ Nullable MediaType mediaType ) {
77
- if (serializer ( GenericTypeResolver . resolveType (type , contextClass ) ) != null ) {
85
+ public boolean canRead (ResolvableType type , @ Nullable MediaType mediaType ) {
86
+ if (! ResolvableType . NONE . equals (type ) && serializer ( type ) != null ) {
78
87
return canRead (mediaType );
79
88
}
80
89
else {
@@ -83,8 +92,8 @@ public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable Med
83
92
}
84
93
85
94
@ Override
86
- public boolean canWrite (@ Nullable Type type , Class <?> clazz , @ Nullable MediaType mediaType ) {
87
- if (serializer ( type != null ? GenericTypeResolver . resolveType (type , clazz ) : clazz ) != null ) {
95
+ public boolean canWrite (ResolvableType type , Class <?> clazz , @ Nullable MediaType mediaType ) {
96
+ if (! ResolvableType . NONE . equals (type ) && serializer ( type ) != null ) {
88
97
return canWrite (mediaType );
89
98
}
90
99
else {
@@ -93,24 +102,12 @@ public boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType
93
102
}
94
103
95
104
@ Override
96
- public final Object read (Type type , @ Nullable Class <?> contextClass , HttpInputMessage inputMessage )
105
+ public final Object read (ResolvableType type , HttpInputMessage inputMessage , @ Nullable Map < String , Object > hints )
97
106
throws IOException , HttpMessageNotReadableException {
98
107
99
- Type resolvedType = GenericTypeResolver .resolveType (type , contextClass );
100
- KSerializer <Object > serializer = serializer (resolvedType );
108
+ KSerializer <Object > serializer = serializer (type );
101
109
if (serializer == null ) {
102
- throw new HttpMessageNotReadableException ("Could not find KSerializer for " + resolvedType , inputMessage );
103
- }
104
- return readInternal (serializer , this .format , inputMessage );
105
- }
106
-
107
- @ Override
108
- protected final Object readInternal (Class <?> clazz , HttpInputMessage inputMessage )
109
- throws IOException , HttpMessageNotReadableException {
110
-
111
- KSerializer <Object > serializer = serializer (clazz );
112
- if (serializer == null ) {
113
- throw new HttpMessageNotReadableException ("Could not find KSerializer for " + clazz , inputMessage );
110
+ throw new HttpMessageNotReadableException ("Could not find KSerializer for " + type , inputMessage );
114
111
}
115
112
return readInternal (serializer , this .format , inputMessage );
116
113
}
@@ -122,13 +119,13 @@ protected abstract Object readInternal(KSerializer<Object> serializer, T format,
122
119
throws IOException , HttpMessageNotReadableException ;
123
120
124
121
@ Override
125
- protected final void writeInternal (Object object , @ Nullable Type type , HttpOutputMessage outputMessage )
126
- throws IOException , HttpMessageNotWritableException {
122
+ protected final void writeInternal (Object object , ResolvableType type , HttpOutputMessage outputMessage ,
123
+ @ Nullable Map < String , Object > hints ) throws IOException , HttpMessageNotWritableException {
127
124
128
- Type resolvedType = type != null ? type : object . getClass ( );
129
- KSerializer <Object > serializer = serializer (resolvedType );
125
+ ResolvableType resolvableType = ( ResolvableType . NONE . equals ( type ) ? ResolvableType . forInstance ( object ) : type );
126
+ KSerializer <Object > serializer = serializer (resolvableType );
130
127
if (serializer == null ) {
131
- throw new HttpMessageNotWritableException ("Could not find KSerializer for " + resolvedType );
128
+ throw new HttpMessageNotWritableException ("Could not find KSerializer for " + resolvableType );
132
129
}
133
130
writeInternal (object , serializer , this .format , outputMessage );
134
131
}
@@ -143,12 +140,38 @@ protected abstract void writeInternal(Object object, KSerializer<Object> seriali
143
140
* Tries to find a serializer that can marshall or unmarshall instances of the given type
144
141
* using kotlinx.serialization. If no serializer can be found, {@code null} is returned.
145
142
* <p>Resolved serializers are cached and cached results are returned on successive calls.
146
- * @param type the type to find a serializer for
143
+ * @param resolvableType the type to find a serializer for
147
144
* @return a resolved serializer for the given type, or {@code null}
148
145
*/
149
146
@ Nullable
150
- private KSerializer <Object > serializer (Type type ) {
151
- KSerializer <Object > serializer = this .serializerCache .get (type );
147
+ private KSerializer <Object > serializer (ResolvableType resolvableType ) {
148
+ if (resolvableType .getSource () instanceof MethodParameter parameter ) {
149
+ Method method = parameter .getMethod ();
150
+ Assert .notNull (method , "Method must not be null" );
151
+ if (KotlinDetector .isKotlinType (method .getDeclaringClass ())) {
152
+ KFunction <?> function = ReflectJvmMapping .getKotlinFunction (method );
153
+ Assert .notNull (function , "Kotlin function must not be null" );
154
+ KType type = (parameter .getParameterIndex () == -1 ? function .getReturnType () :
155
+ KCallables .getValueParameters (function ).get (parameter .getParameterIndex ()).getType ());
156
+ KSerializer <Object > serializer = this .kTypeSerializerCache .get (type );
157
+ if (serializer == null ) {
158
+ try {
159
+ serializer = SerializersKt .serializerOrNull (this .format .getSerializersModule (), type );
160
+ }
161
+ catch (IllegalArgumentException ignored ) {
162
+ }
163
+ if (serializer != null ) {
164
+ if (hasPolymorphism (serializer .getDescriptor (), new HashSet <>())) {
165
+ return null ;
166
+ }
167
+ this .kTypeSerializerCache .put (type , serializer );
168
+ }
169
+ }
170
+ return serializer ;
171
+ }
172
+ }
173
+ Type type = resolvableType .getType ();
174
+ KSerializer <Object > serializer = this .typeSerializerCache .get (type );
152
175
if (serializer == null ) {
153
176
try {
154
177
serializer = SerializersKt .serializerOrNull (this .format .getSerializersModule (), type );
@@ -159,7 +182,7 @@ private KSerializer<Object> serializer(Type type) {
159
182
if (hasPolymorphism (serializer .getDescriptor (), new HashSet <>())) {
160
183
return null ;
161
184
}
162
- this .serializerCache .put (type , serializer );
185
+ this .typeSerializerCache .put (type , serializer );
163
186
}
164
187
}
165
188
return serializer ;
0 commit comments