提交 74158af1 编写于 作者: S Sebastien Deleuze

Add JsonView and type resolution support to JacksonJsonDecoder

There is no contextClass support yet, we need to find a way to pass
this information to the codecs.

Issue: SPR-14158
上级 903770f0
......@@ -17,21 +17,23 @@
package org.springframework.http.codec.json;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.CodecException;
import org.springframework.core.codec.AbstractDecoder;
import org.springframework.core.codec.Decoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.support.DataBufferUtils;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
......@@ -44,15 +46,7 @@ import org.springframework.util.MimeType;
* @since 5.0
* @see JacksonJsonEncoder
*/
public class JacksonJsonDecoder extends AbstractDecoder<Object> {
private static final MimeType[] MIME_TYPES = new MimeType[] {
new MimeType("application", "json", StandardCharsets.UTF_8),
new MimeType("application", "*+json", StandardCharsets.UTF_8)
};
private final ObjectMapper mapper;
public class JacksonJsonDecoder extends AbstractJacksonJsonCodec implements Decoder<Object> {
private final JsonObjectDecoder fluxObjectDecoder = new JsonObjectDecoder(true);
......@@ -60,14 +54,25 @@ public class JacksonJsonDecoder extends AbstractDecoder<Object> {
public JacksonJsonDecoder() {
this(new ObjectMapper());
super(Jackson2ObjectMapperBuilder.json().build());
}
public JacksonJsonDecoder(ObjectMapper mapper) {
super(MIME_TYPES);
this.mapper = mapper;
super(mapper);
}
@Override
public boolean canDecode(ResolvableType elementType, MimeType mimeType, Object... hints) {
if (mimeType == null) {
return true;
}
return JSON_MIME_TYPES.stream().anyMatch(m -> m.isCompatibleWith(mimeType));
}
@Override
public List<MimeType> getDecodableMimeTypes() {
return JSON_MIME_TYPES;
}
@Override
public Flux<Object> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
......@@ -91,10 +96,24 @@ public class JacksonJsonDecoder extends AbstractDecoder<Object> {
Assert.notNull(inputStream, "'inputStream' must not be null");
Assert.notNull(elementType, "'elementType' must not be null");
TypeFactory typeFactory = this.mapper.getTypeFactory();
JavaType javaType = typeFactory.constructType(elementType.getType());
ObjectReader reader = this.mapper.readerFor(javaType);
MethodParameter methodParameter = (elementType.getSource() instanceof MethodParameter ?
(MethodParameter)elementType.getSource() : null);
// TODO Find a way to pass the real concrete controller contextClass
JavaType javaType = getJavaType(elementType.getType(), null);
ObjectReader reader;
if (methodParameter != null && methodParameter.getParameter().getAnnotation(JsonView.class) != null) {
JsonView annotation = methodParameter.getParameter().getAnnotation(JsonView.class);
Class<?>[] classes = annotation.value();
if (classes.length != 1) {
throw new IllegalArgumentException(
"@JsonView only supported for response body advice with exactly 1 class argument: " + methodParameter);
}
reader = mapper.readerWithView(classes[0]).forType(javaType);
}
else {
reader = mapper.readerFor(javaType);
}
return objectDecoder.decode(inputStream, elementType, mimeType, hints)
.map(dataBuffer -> {
......
......@@ -20,6 +20,8 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonView;
import static org.junit.Assert.assertNull;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
......@@ -84,7 +86,74 @@ public class JacksonJsonDecoderTests extends AbstractDataBufferAllocatingTestCas
assertValues(new Pojo("f1", "b1"), new Pojo("f2", "b2"));
}
@Test
public void jsonView() throws Exception {
Flux<DataBuffer> source = Flux.just(
stringBuffer("{\"withView1\" : \"with\", \"withView2\" : \"with\", \"withoutView\" : \"without\"}"));
ResolvableType elementType = ResolvableType
.forMethodParameter(JacksonController.class.getMethod("foo", JacksonViewBean.class), 0);
Flux<JacksonViewBean> flux = new JacksonJsonDecoder()
.decode(source, elementType, null).cast(JacksonViewBean.class);
TestSubscriber
.subscribe(flux)
.assertNoError()
.assertComplete()
.assertValuesWith(b -> {
assertTrue(b.getWithView1().equals("with"));
assertNull(b.getWithView2());
assertNull(b.getWithoutView());
});
}
void handle(List<Pojo> list) {
}
private interface MyJacksonView1 {}
private interface MyJacksonView2 {}
@SuppressWarnings("unused")
private static class JacksonViewBean {
@JsonView(MyJacksonView1.class)
private String withView1;
@JsonView(MyJacksonView2.class)
private String withView2;
private String withoutView;
public String getWithView1() {
return withView1;
}
public void setWithView1(String withView1) {
this.withView1 = withView1;
}
public String getWithView2() {
return withView2;
}
public void setWithView2(String withView2) {
this.withView2 = withView2;
}
public String getWithoutView() {
return withoutView;
}
public void setWithoutView(String withoutView) {
this.withoutView = withoutView;
}
}
private static class JacksonController {
public JacksonViewBean foo(@JsonView(MyJacksonView1.class) JacksonViewBean bean) {
return bean;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册