Browse Source

Fix FilterProvider handling in AbstractJacksonEncoder

Closes gh-35575
pull/35588/head
Sébastien Deleuze 6 months ago
parent
commit
891c3e3a1f
  1. 24
      spring-web/src/main/java/org/springframework/http/codec/AbstractJacksonEncoder.java
  2. 54
      spring-web/src/test/java/org/springframework/http/codec/json/JacksonJsonEncoderTests.java

24
spring-web/src/main/java/org/springframework/http/codec/AbstractJacksonEncoder.java

@ -161,7 +161,7 @@ public abstract class AbstractJacksonEncoder<T extends ObjectMapper> extends Jac @@ -161,7 +161,7 @@ public abstract class AbstractJacksonEncoder<T extends ObjectMapper> extends Jac
throw new IllegalStateException("No ObjectMapper for " + elementType);
}
ObjectWriter writer = createObjectWriter(mapper, elementType, mimeType, null, hintsToUse);
ObjectWriter writer = createObjectWriter(mapper, elementType, mimeType, hintsToUse);
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.generatorFactory()._getBufferRecycler());
JsonEncoding encoding = getJsonEncoding(mimeType);
JsonGenerator generator = mapper.createGenerator(byteBuilder, encoding);
@ -219,22 +219,12 @@ public abstract class AbstractJacksonEncoder<T extends ObjectMapper> extends Jac @@ -219,22 +219,12 @@ public abstract class AbstractJacksonEncoder<T extends ObjectMapper> extends Jac
public DataBuffer encodeValue(Object value, DataBufferFactory bufferFactory,
ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
Class<?> jsonView = null;
FilterProvider filters = null;
if (hints != null) {
jsonView = (Class<?>) hints.get(JSON_VIEW_HINT);
filters = (FilterProvider) hints.get(FILTER_PROVIDER_HINT);
}
T mapper = selectMapper(valueType, mimeType);
if (mapper == null) {
throw new IllegalStateException("No ObjectMapper for " + valueType);
}
ObjectWriter writer = createObjectWriter(mapper, valueType, mimeType, jsonView, hints);
if (filters != null) {
writer = writer.with(filters);
}
ObjectWriter writer = createObjectWriter(mapper, valueType, mimeType, hints);
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.generatorFactory()._getBufferRecycler());
try {
@ -321,13 +311,19 @@ public abstract class AbstractJacksonEncoder<T extends ObjectMapper> extends Jac @@ -321,13 +311,19 @@ public abstract class AbstractJacksonEncoder<T extends ObjectMapper> extends Jac
private ObjectWriter createObjectWriter(
T mapper, ResolvableType valueType, @Nullable MimeType mimeType,
@Nullable Class<?> jsonView, @Nullable Map<String, Object> hints) {
@Nullable Map<String, Object> hints) {
JavaType javaType = getJavaType(valueType.getType(), null);
if (jsonView == null && hints != null) {
Class<?> jsonView = null;
FilterProvider filters = null;
if (hints != null) {
jsonView = (Class<?>) hints.get(JacksonCodecSupport.JSON_VIEW_HINT);
filters = (FilterProvider) hints.get(FILTER_PROVIDER_HINT);
}
ObjectWriter writer = (jsonView != null ? mapper.writerWithView(jsonView) : mapper.writer());
if (filters != null) {
writer = writer.with(filters);
}
if (javaType.isContainerType()) {
writer = writer.forType(javaType);
}

54
spring-web/src/test/java/org/springframework/http/codec/json/JacksonJsonEncoderTests.java

@ -22,6 +22,7 @@ import java.util.Arrays; @@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import org.junit.jupiter.api.Test;
@ -30,6 +31,9 @@ import reactor.core.publisher.Mono; @@ -30,6 +31,9 @@ import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import tools.jackson.databind.SerializationFeature;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.ser.FilterProvider;
import tools.jackson.databind.ser.std.SimpleBeanPropertyFilter;
import tools.jackson.databind.ser.std.SimpleFilterProvider;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
@ -50,6 +54,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON; @@ -50,6 +54,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.APPLICATION_NDJSON;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
import static org.springframework.http.MediaType.APPLICATION_XML;
import static org.springframework.http.codec.JacksonCodecSupport.FILTER_PROVIDER_HINT;
import static org.springframework.http.codec.JacksonCodecSupport.JSON_VIEW_HINT;
/**
@ -211,6 +216,24 @@ class JacksonJsonEncoderTests extends AbstractEncoderTests<JacksonJsonEncoder> { @@ -211,6 +216,24 @@ class JacksonJsonEncoderTests extends AbstractEncoderTests<JacksonJsonEncoder> {
);
}
@Test
void fieldLevelJsonViewStream() {
JacksonViewBean bean = new JacksonViewBean();
bean.setWithView1("with");
bean.setWithView2("with");
bean.setWithoutView("without");
Flux<JacksonViewBean> input = Flux.just(bean, bean);
ResolvableType type = ResolvableType.forClass(JacksonViewBean.class);
Map<String, Object> hints = singletonMap(JSON_VIEW_HINT, MyJacksonView1.class);
testEncodeAll(input, type, APPLICATION_NDJSON, hints, step -> step
.consumeNextWith(expectString("{\"withView1\":\"with\"}\n"))
.consumeNextWith(expectString("{\"withView1\":\"with\"}\n"))
.verifyComplete()
);
}
@Test
void classLevelJsonView() {
JacksonViewBean bean = new JacksonViewBean();
@ -228,6 +251,33 @@ class JacksonJsonEncoderTests extends AbstractEncoderTests<JacksonJsonEncoder> { @@ -228,6 +251,33 @@ class JacksonJsonEncoderTests extends AbstractEncoderTests<JacksonJsonEncoder> {
);
}
@Test
void filterProvider() {
JacksonFilteredBean filteredBean = new JacksonFilteredBean("foo", "bar");
FilterProvider filters = new SimpleFilterProvider().addFilter("myJacksonFilter",
SimpleBeanPropertyFilter.serializeAllExcept("property2"));
Mono<JacksonFilteredBean> input = Mono.just(filteredBean);
Map<String, Object> hints = singletonMap(FILTER_PROVIDER_HINT, filters);
testEncode(input, ResolvableType.forClass(Pojo.class), APPLICATION_JSON, hints, step -> step
.consumeNextWith(expectString("{\"property1\":\"foo\"}"))
.verifyComplete()
);
}
@Test
void filterProviderStream() {
JacksonFilteredBean filteredBean = new JacksonFilteredBean("foo", "bar");
FilterProvider filters = new SimpleFilterProvider().addFilter("myJacksonFilter",
SimpleBeanPropertyFilter.serializeAllExcept("property2"));
Flux<JacksonFilteredBean> input = Flux.just(filteredBean, filteredBean);
Map<String, Object> hints = singletonMap(FILTER_PROVIDER_HINT, filters);
testEncodeAll(input, ResolvableType.forClass(Pojo.class), APPLICATION_NDJSON, hints, step -> step
.consumeNextWith(expectString("{\"property1\":\"foo\"}\n"))
.consumeNextWith(expectString("{\"property1\":\"foo\"}\n"))
.verifyComplete()
);
}
@Test // gh-22771
public void encodeWithFlushAfterWriteOff() {
JsonMapper mapper = JsonMapper.builder().configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, false).build();
@ -267,4 +317,8 @@ class JacksonJsonEncoderTests extends AbstractEncoderTests<JacksonJsonEncoder> { @@ -267,4 +317,8 @@ class JacksonJsonEncoderTests extends AbstractEncoderTests<JacksonJsonEncoder> {
private static class Bar extends ParentClass {
}
@JsonFilter("myJacksonFilter")
record JacksonFilteredBean(String property1, String property2) {
}
}

Loading…
Cancel
Save