23
23
import java .util .Arrays ;
24
24
import java .util .Collections ;
25
25
import java .util .List ;
26
+ import java .util .Optional ;
26
27
27
28
import org .apache .commons .logging .Log ;
28
29
30
+ import org .springframework .core .GenericTypeResolver ;
29
31
import org .springframework .http .HttpHeaders ;
30
32
import org .springframework .http .HttpInputMessage ;
31
33
import org .springframework .http .HttpLogging ;
45
47
* @author Arjen Poutsma
46
48
* @author Juergen Hoeller
47
49
* @author Sebastien Deleuze
50
+ * @author Vladislav Kisel
48
51
* @since 3.0
49
52
* @param <T> the converted object type
50
53
*/
@@ -53,17 +56,23 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv
53
56
/** Logger available to subclasses. */
54
57
protected final Log logger = HttpLogging .forLogName (getClass ());
55
58
59
+ /**
60
+ * This converter type argument
61
+ */
62
+ @ Nullable
63
+ private Class <?> typeArgument ;
64
+
56
65
private List <MediaType > supportedMediaTypes = Collections .emptyList ();
57
66
58
67
@ Nullable
59
68
private Charset defaultCharset ;
60
69
61
-
62
70
/**
63
71
* Construct an {@code AbstractHttpMessageConverter} with no supported media types.
64
72
* @see #setSupportedMediaTypes
65
73
*/
66
74
protected AbstractHttpMessageConverter () {
75
+ resolveTypeArgument ();
67
76
}
68
77
69
78
/**
@@ -72,6 +81,7 @@ protected AbstractHttpMessageConverter() {
72
81
*/
73
82
protected AbstractHttpMessageConverter (MediaType supportedMediaType ) {
74
83
setSupportedMediaTypes (Collections .singletonList (supportedMediaType ));
84
+ resolveTypeArgument ();
75
85
}
76
86
77
87
/**
@@ -80,6 +90,7 @@ protected AbstractHttpMessageConverter(MediaType supportedMediaType) {
80
90
*/
81
91
protected AbstractHttpMessageConverter (MediaType ... supportedMediaTypes ) {
82
92
setSupportedMediaTypes (Arrays .asList (supportedMediaTypes ));
93
+ resolveTypeArgument ();
83
94
}
84
95
85
96
/**
@@ -92,6 +103,7 @@ protected AbstractHttpMessageConverter(MediaType... supportedMediaTypes) {
92
103
protected AbstractHttpMessageConverter (Charset defaultCharset , MediaType ... supportedMediaTypes ) {
93
104
this .defaultCharset = defaultCharset ;
94
105
setSupportedMediaTypes (Arrays .asList (supportedMediaTypes ));
106
+ resolveTypeArgument ();
95
107
}
96
108
97
109
@@ -207,8 +219,9 @@ public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
207
219
public final void write (final T t , @ Nullable MediaType contentType , HttpOutputMessage outputMessage )
208
220
throws IOException , HttpMessageNotWritableException {
209
221
222
+ final T unboxed = unboxIfNeeded (t );
210
223
final HttpHeaders headers = outputMessage .getHeaders ();
211
- addDefaultHeaders (headers , t , contentType );
224
+ addDefaultHeaders (headers , unboxed , contentType );
212
225
213
226
if (outputMessage instanceof StreamingHttpOutputMessage streamingOutputMessage ) {
214
227
streamingOutputMessage .setBody (outputStream -> writeInternal (t , new HttpOutputMessage () {
@@ -223,11 +236,26 @@ public HttpHeaders getHeaders() {
223
236
}));
224
237
}
225
238
else {
226
- writeInternal (t , outputMessage );
239
+ writeInternal (unboxed , outputMessage );
227
240
outputMessage .getBody ().flush ();
228
241
}
229
242
}
230
243
244
+ /**
245
+ * Unbox the given object if it is stored in a container (like {@link java.util.Optional}.
246
+ */
247
+ @ SuppressWarnings ("unchecked" )
248
+ protected T unboxIfNeeded (T value ) {
249
+ // Skip unboxing if the type is unknown to ensure type safety
250
+ if (typeArgument != null ) {
251
+ if (value instanceof Optional && !typeArgument .equals (Optional .class )) {
252
+ return ((Optional <T >) value ).orElse (value );
253
+ }
254
+ }
255
+
256
+ return value ;
257
+ }
258
+
231
259
/**
232
260
* Add default headers to the output message.
233
261
* <p>This implementation delegates to {@link #getDefaultContentType(Object)} if a
@@ -319,4 +347,11 @@ protected abstract T readInternal(Class<? extends T> clazz, HttpInputMessage inp
319
347
protected abstract void writeInternal (T t , HttpOutputMessage outputMessage )
320
348
throws IOException , HttpMessageNotWritableException ;
321
349
350
+ /**
351
+ * Resolve generic type of this converter and set it to {@link this#typeArgument}
352
+ */
353
+ private void resolveTypeArgument () {
354
+ this .typeArgument = GenericTypeResolver .resolveTypeArgument (this .getClass (), AbstractHttpMessageConverter .class );
355
+ }
356
+
322
357
}
0 commit comments