Improve support for generics in Jackson codecs
Closes gh-23791
This commit is contained in:
parent
77517d6cff
commit
992e75303d
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -153,8 +153,10 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
|
||||||
|
|
||||||
private ObjectReader getObjectReader(ResolvableType elementType, @Nullable Map<String, Object> hints) {
|
private ObjectReader getObjectReader(ResolvableType elementType, @Nullable Map<String, Object> hints) {
|
||||||
Assert.notNull(elementType, "'elementType' must not be null");
|
Assert.notNull(elementType, "'elementType' must not be null");
|
||||||
MethodParameter param = getParameter(elementType);
|
Class<?> contextClass = getContextClass(elementType);
|
||||||
Class<?> contextClass = (param != null ? param.getContainingClass() : null);
|
if (contextClass == null && hints != null) {
|
||||||
|
contextClass = getContextClass((ResolvableType) hints.get(ACTUAL_TYPE_HINT));
|
||||||
|
}
|
||||||
JavaType javaType = getJavaType(elementType.getType(), contextClass);
|
JavaType javaType = getJavaType(elementType.getType(), contextClass);
|
||||||
Class<?> jsonView = (hints != null ? (Class<?>) hints.get(Jackson2CodecSupport.JSON_VIEW_HINT) : null);
|
Class<?> jsonView = (hints != null ? (Class<?>) hints.get(Jackson2CodecSupport.JSON_VIEW_HINT) : null);
|
||||||
return jsonView != null ?
|
return jsonView != null ?
|
||||||
|
|
@ -162,6 +164,12 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
|
||||||
getObjectMapper().readerFor(javaType);
|
getObjectMapper().readerFor(javaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Class<?> getContextClass(@Nullable ResolvableType elementType) {
|
||||||
|
MethodParameter param = (elementType != null ? getParameter(elementType) : null);
|
||||||
|
return (param != null ? param.getContainingClass() : null);
|
||||||
|
}
|
||||||
|
|
||||||
private void logValue(@Nullable Object value, @Nullable Map<String, Object> hints) {
|
private void logValue(@Nullable Object value, @Nullable Map<String, Object> hints) {
|
||||||
if (!Hints.isLoggingSuppressed(hints)) {
|
if (!Hints.isLoggingSuppressed(hints)) {
|
||||||
LogFormatUtils.traceDebug(logger, traceOn -> {
|
LogFormatUtils.traceDebug(logger, traceOn -> {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
@ -34,6 +35,8 @@ import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.codec.Hints;
|
import org.springframework.core.codec.Hints;
|
||||||
import org.springframework.http.HttpLogging;
|
import org.springframework.http.HttpLogging;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.MimeType;
|
import org.springframework.util.MimeType;
|
||||||
|
|
@ -55,6 +58,15 @@ public abstract class Jackson2CodecSupport {
|
||||||
*/
|
*/
|
||||||
public static final String JSON_VIEW_HINT = Jackson2CodecSupport.class.getName() + ".jsonView";
|
public static final String JSON_VIEW_HINT = Jackson2CodecSupport.class.getName() + ".jsonView";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key for the hint to access the actual ResolvableType passed into
|
||||||
|
* {@link org.springframework.http.codec.HttpMessageReader#read(ResolvableType, ResolvableType, ServerHttpRequest, ServerHttpResponse, Map)}
|
||||||
|
* (server-side only). Currently set when the method argument has generics because
|
||||||
|
* in case of reactive types, use of {@code ResolvableType.getGeneric()} means no
|
||||||
|
* MethodParameter source and no knowledge of the containing class.
|
||||||
|
*/
|
||||||
|
static final String ACTUAL_TYPE_HINT = Jackson2CodecSupport.class.getName() + ".actualType";
|
||||||
|
|
||||||
private static final String JSON_VIEW_HINT_ERROR =
|
private static final String JSON_VIEW_HINT_ERROR =
|
||||||
"@JsonView only supported for write hints with exactly 1 class argument: ";
|
"@JsonView only supported for write hints with exactly 1 class argument: ";
|
||||||
|
|
||||||
|
|
@ -106,11 +118,20 @@ public abstract class Jackson2CodecSupport {
|
||||||
protected Map<String, Object> getHints(ResolvableType resolvableType) {
|
protected Map<String, Object> getHints(ResolvableType resolvableType) {
|
||||||
MethodParameter param = getParameter(resolvableType);
|
MethodParameter param = getParameter(resolvableType);
|
||||||
if (param != null) {
|
if (param != null) {
|
||||||
|
Map<String, Object> hints = null;
|
||||||
|
if (resolvableType.hasGenerics()) {
|
||||||
|
hints = new HashMap<>(2);
|
||||||
|
hints.put(ACTUAL_TYPE_HINT, resolvableType);
|
||||||
|
}
|
||||||
JsonView annotation = getAnnotation(param, JsonView.class);
|
JsonView annotation = getAnnotation(param, JsonView.class);
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
Class<?>[] classes = annotation.value();
|
Class<?>[] classes = annotation.value();
|
||||||
Assert.isTrue(classes.length == 1, JSON_VIEW_HINT_ERROR + param);
|
Assert.isTrue(classes.length == 1, JSON_VIEW_HINT_ERROR + param);
|
||||||
return Hints.from(JSON_VIEW_HINT, classes[0]);
|
hints = (hints != null ? hints : new HashMap<>(1));
|
||||||
|
hints.put(JSON_VIEW_HINT, classes[0]);
|
||||||
|
}
|
||||||
|
if (hints != null) {
|
||||||
|
return hints;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Hints.none();
|
return Hints.none();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -28,7 +28,6 @@ import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
import io.reactivex.Flowable;
|
import io.reactivex.Flowable;
|
||||||
import io.reactivex.Maybe;
|
import io.reactivex.Maybe;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
@ -522,7 +521,6 @@ public class RequestMappingMessageConversionIntegrationTests extends AbstractReq
|
||||||
assertThat(getApplicationContext().getBean(PersonCreateController.class).persons.size()).isEqualTo(2);
|
assertThat(getApplicationContext().getBean(PersonCreateController.class).persons.size()).isEqualTo(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Disabled
|
|
||||||
@ParameterizedHttpServerTest // gh-23791
|
@ParameterizedHttpServerTest // gh-23791
|
||||||
public void personCreateViaDefaultMethodWithGenerics(HttpServer httpServer) throws Exception {
|
public void personCreateViaDefaultMethodWithGenerics(HttpServer httpServer) throws Exception {
|
||||||
startServer(httpServer);
|
startServer(httpServer);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue