Browse Source

Use specified ResolvableType in JacksonJsonEncoder

This commit also fixes an issue in the HTTP client that used the
wrapper type instead of the element type. As a consequence, due
to type erasure, we now have to specify the type of the content
in DefaultHttpRequestBuilder#contentStream().
pull/1111/head
Sebastien Deleuze 10 years ago
parent
commit
a0e2231779
  1. 18
      spring-web-reactive/src/main/java/org/springframework/core/codec/support/JacksonJsonEncoder.java
  2. 13
      spring-web-reactive/src/main/java/org/springframework/web/client/reactive/DefaultHttpRequestBuilder.java
  3. 37
      spring-web-reactive/src/test/java/org/springframework/core/codec/support/JacksonJsonEncoderTests.java

18
spring-web-reactive/src/main/java/org/springframework/core/codec/support/JacksonJsonEncoder.java

@ -21,7 +21,10 @@ import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.type.TypeFactory;
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;
@ -66,10 +69,13 @@ public class JacksonJsonEncoder extends AbstractEncoder<Object> {
public Flux<DataBuffer> encode(Publisher<?> inputStream, public Flux<DataBuffer> encode(Publisher<?> inputStream,
DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType,
Object... hints) { Object... hints) {
Assert.notNull(inputStream, "'inputStream' must not be null");
Assert.notNull(bufferFactory, "'bufferFactory' must not be null");
Assert.notNull(elementType, "'elementType' must not be null");
if (inputStream instanceof Mono) { if (inputStream instanceof Mono) {
// single object // single object
return Flux.from(inputStream) return Flux.from(inputStream)
.map(value -> serialize(value, bufferFactory)); .map(value -> serialize(value, bufferFactory, elementType));
} }
else { else {
// array // array
@ -81,7 +87,7 @@ public class JacksonJsonEncoder extends AbstractEncoder<Object> {
Mono.just(bufferFactory.wrap(END_ARRAY_BUFFER)); Mono.just(bufferFactory.wrap(END_ARRAY_BUFFER));
Flux<DataBuffer> serializedObjects = Flux.from(inputStream) Flux<DataBuffer> serializedObjects = Flux.from(inputStream)
.map(value -> serialize(value, bufferFactory)); .map(value -> serialize(value, bufferFactory, elementType));
Flux<DataBuffer> array = Flux.zip(serializedObjects, arraySeparators) Flux<DataBuffer> array = Flux.zip(serializedObjects, arraySeparators)
.flatMap(tuple -> Flux.just(tuple.getT1(), tuple.getT2())); .flatMap(tuple -> Flux.just(tuple.getT1(), tuple.getT2()));
@ -92,11 +98,15 @@ public class JacksonJsonEncoder extends AbstractEncoder<Object> {
} }
} }
private DataBuffer serialize(Object value, DataBufferFactory dataBufferFactory) { private DataBuffer serialize(Object value, DataBufferFactory dataBufferFactory,
ResolvableType type) {
TypeFactory typeFactory = this.mapper.getTypeFactory();
JavaType javaType = typeFactory.constructType(type.getType());
ObjectWriter writer = this.mapper.writerFor(javaType);
DataBuffer buffer = dataBufferFactory.allocateBuffer(); DataBuffer buffer = dataBufferFactory.allocateBuffer();
OutputStream outputStream = buffer.asOutputStream(); OutputStream outputStream = buffer.asOutputStream();
try { try {
this.mapper.writeValue(outputStream, value); writer.writeValue(outputStream, value);
} }
catch (IOException e) { catch (IOException e) {
throw new CodecException("Error while writing the data", e); throw new CodecException("Error while writing the data", e);

13
spring-web-reactive/src/main/java/org/springframework/web/client/reactive/DefaultHttpRequestBuilder.java

@ -60,6 +60,8 @@ public class DefaultHttpRequestBuilder implements HttpRequestBuilder {
protected Publisher contentPublisher; protected Publisher contentPublisher;
protected ResolvableType contentType;
protected final List<HttpCookie> cookies = new ArrayList<HttpCookie>(); protected final List<HttpCookie> cookies = new ArrayList<HttpCookie>();
protected DefaultHttpRequestBuilder() { protected DefaultHttpRequestBuilder() {
@ -111,11 +113,13 @@ public class DefaultHttpRequestBuilder implements HttpRequestBuilder {
public DefaultHttpRequestBuilder content(Object content) { public DefaultHttpRequestBuilder content(Object content) {
this.contentPublisher = Mono.just(content); this.contentPublisher = Mono.just(content);
this.contentType = ResolvableType.forInstance(content);
return this; return this;
} }
public DefaultHttpRequestBuilder contentStream(Publisher<?> content) { public DefaultHttpRequestBuilder contentStream(Publisher<?> content, ResolvableType type) {
this.contentPublisher = Flux.from(content); this.contentPublisher = Flux.from(content);
this.contentType = type;
return this; return this;
} }
@ -139,22 +143,21 @@ public class DefaultHttpRequestBuilder implements HttpRequestBuilder {
request.getHeaders().putAll(this.httpHeaders); request.getHeaders().putAll(this.httpHeaders);
if (this.contentPublisher != null) { if (this.contentPublisher != null) {
ResolvableType requestBodyType = ResolvableType.forInstance(this.contentPublisher);
MediaType mediaType = request.getHeaders().getContentType(); MediaType mediaType = request.getHeaders().getContentType();
Optional<Encoder<?>> messageEncoder = messageEncoders Optional<Encoder<?>> messageEncoder = messageEncoders
.stream() .stream()
.filter(e -> e.canEncode(requestBodyType, mediaType)) .filter(e -> e.canEncode(this.contentType, mediaType))
.findFirst(); .findFirst();
if (messageEncoder.isPresent()) { if (messageEncoder.isPresent()) {
request.writeWith(messageEncoder.get() request.writeWith(messageEncoder.get()
.encode(this.contentPublisher, request.bufferFactory(), .encode(this.contentPublisher, request.bufferFactory(),
requestBodyType, mediaType)); this.contentType, mediaType));
} }
else { else {
throw new WebClientException("Can't write request body " + throw new WebClientException("Can't write request body " +
"of type '" + requestBodyType.toString() + "of type '" + this.contentType.toString() +
"' for content-type '" + mediaType.toString() + "'"); "' for content-type '" + mediaType.toString() + "'");
} }
} }

37
spring-web-reactive/src/test/java/org/springframework/core/codec/support/JacksonJsonEncoderTests.java

@ -16,11 +16,14 @@
package org.springframework.core.codec.support; package org.springframework.core.codec.support;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.test.TestSubscriber; import reactor.core.test.TestSubscriber;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase; import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -50,8 +53,9 @@ public class JacksonJsonEncoderTests extends AbstractDataBufferAllocatingTestCas
public void write() { public void write() {
Flux<Pojo> source = Flux.just(new Pojo("foofoo", "barbar"), new Pojo("foofoofoo", "barbarbar")); Flux<Pojo> source = Flux.just(new Pojo("foofoo", "barbar"), new Pojo("foofoofoo", "barbarbar"));
ResolvableType type = ResolvableType.forClass(Pojo.class);
Flux<DataBuffer> output = Flux<DataBuffer> output =
this.encoder.encode(source, this.dataBufferFactory, null, null); this.encoder.encode(source, this.dataBufferFactory, type, null);
TestSubscriber TestSubscriber
.subscribe(output) .subscribe(output)
@ -64,4 +68,35 @@ public class JacksonJsonEncoderTests extends AbstractDataBufferAllocatingTestCas
stringConsumer("]")); stringConsumer("]"));
} }
@Test
public void writeWithType() {
Flux<ParentClass> source = Flux.just(new Foo(), new Bar());
ResolvableType type = ResolvableType.forClass(ParentClass.class);
Flux<DataBuffer> output =
this.encoder.encode(source, this.dataBufferFactory, type, null);
TestSubscriber
.subscribe(output)
.assertComplete()
.assertNoError()
.assertValuesWith(stringConsumer("["),
stringConsumer("{\"type\":\"foo\"}"),
stringConsumer(","),
stringConsumer("{\"type\":\"bar\"}"),
stringConsumer("]"));
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
private static class ParentClass {
}
@JsonTypeName("foo")
private static class Foo extends ParentClass {
}
@JsonTypeName("bar")
private static class Bar extends ParentClass {
}
} }

Loading…
Cancel
Save