Take into account the MimeType's charset in Jackson encoder
Notice that per specification, only Unicode is supported (UTF8, UTF16_BE, UTF16_LE, UTF32_BE, UTF32_LE). Issue: SPR-16539
This commit is contained in:
parent
99bb97388e
commit
36a222acd5
|
|
@ -19,12 +19,15 @@ package org.springframework.http.codec.json;
|
|||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonEncoding;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
|
@ -109,16 +112,18 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
|||
Assert.notNull(bufferFactory, "'bufferFactory' must not be null");
|
||||
Assert.notNull(elementType, "'elementType' must not be null");
|
||||
|
||||
JsonEncoding encoding = getJsonEncoding(mimeType);
|
||||
|
||||
if (inputStream instanceof Mono) {
|
||||
return Flux.from(inputStream).map(value ->
|
||||
encodeValue(value, mimeType, bufferFactory, elementType, hints));
|
||||
encodeValue(value, mimeType, bufferFactory, elementType, hints, encoding));
|
||||
}
|
||||
|
||||
for (MediaType streamingMediaType : this.streamingMediaTypes) {
|
||||
if (streamingMediaType.isCompatibleWith(mimeType)) {
|
||||
byte[] separator = STREAM_SEPARATORS.getOrDefault(streamingMediaType, NEWLINE_SEPARATOR);
|
||||
return Flux.from(inputStream).map(value -> {
|
||||
DataBuffer buffer = encodeValue(value, mimeType, bufferFactory, elementType, hints);
|
||||
DataBuffer buffer = encodeValue(value, mimeType, bufferFactory, elementType, hints, encoding);
|
||||
if (separator != null) {
|
||||
buffer.write(separator);
|
||||
}
|
||||
|
|
@ -129,11 +134,11 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
|||
|
||||
ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType);
|
||||
return Flux.from(inputStream).collectList().map(list ->
|
||||
encodeValue(list, mimeType, bufferFactory, listType, hints)).flux();
|
||||
encodeValue(list, mimeType, bufferFactory, listType, hints, encoding)).flux();
|
||||
}
|
||||
|
||||
private DataBuffer encodeValue(Object value, @Nullable MimeType mimeType, DataBufferFactory bufferFactory,
|
||||
ResolvableType elementType, @Nullable Map<String, Object> hints) {
|
||||
ResolvableType elementType, @Nullable Map<String, Object> hints, JsonEncoding encoding) {
|
||||
|
||||
JavaType javaType = getJavaType(elementType.getType(), null);
|
||||
Class<?> jsonView = (hints != null ? (Class<?>) hints.get(Jackson2CodecSupport.JSON_VIEW_HINT) : null);
|
||||
|
|
@ -148,8 +153,10 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
|||
|
||||
DataBuffer buffer = bufferFactory.allocateBuffer();
|
||||
OutputStream outputStream = buffer.asOutputStream();
|
||||
|
||||
try {
|
||||
writer.writeValue(outputStream, value);
|
||||
JsonGenerator generator = getObjectMapper().getFactory().createGenerator(outputStream, encoding);
|
||||
writer.writeValue(generator, value);
|
||||
}
|
||||
catch (InvalidDefinitionException ex) {
|
||||
throw new CodecException("Type definition error: " + ex.getType(), ex);
|
||||
|
|
@ -170,6 +177,24 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
|||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the JSON encoding to use for the given mime type.
|
||||
* @param mimeType the mime type as requested by the caller
|
||||
* @return the JSON encoding to use (never {@code null})
|
||||
* @since 5.0.5
|
||||
*/
|
||||
protected JsonEncoding getJsonEncoding(@Nullable MimeType mimeType) {
|
||||
if (mimeType != null && mimeType.getCharset() != null) {
|
||||
Charset charset = mimeType.getCharset();
|
||||
for (JsonEncoding encoding : JsonEncoding.values()) {
|
||||
if (charset.name().equals(encoding.getJavaName())) {
|
||||
return encoding;
|
||||
}
|
||||
}
|
||||
}
|
||||
return JsonEncoding.UTF8;
|
||||
}
|
||||
|
||||
|
||||
// HttpMessageEncoder...
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import java.util.Collections;
|
|||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
|
@ -154,20 +153,18 @@ public class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAll
|
|||
.verify();
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test // SPR-16516, SPR-16539
|
||||
public void writePojoWithCustomEncoding() {
|
||||
Flux<Pojo> source = Flux.just(new Pojo("foo\u00A3", "bar\u00A3"));
|
||||
Charset charset = StandardCharsets.ISO_8859_1;
|
||||
Flux<Pojo> source = Flux.just(new Pojo("foo\uD834\uDD1E", "bar\uD834\uDD1E"));
|
||||
Charset charset = StandardCharsets.UTF_16LE;
|
||||
MediaType mediaType = new MediaType("text", "event-stream", charset);
|
||||
MockServerHttpResponse outputMessage = new MockServerHttpResponse();
|
||||
testWrite(source, mediaType, outputMessage, Pojo.class);
|
||||
|
||||
assertEquals(mediaType, outputMessage.getHeaders().getContentType());
|
||||
StepVerifier.create(outputMessage.getBodyAsString())
|
||||
.expectNext("data:{\"foo\":\"foo\u00A3\",\"bar\":\"bar\u00A3\"}\n\n")
|
||||
.expectComplete()
|
||||
.verify();
|
||||
.expectNext("data:{\"foo\":\"foo\uD834\uDD1E\",\"bar\":\"bar\uD834\uDD1E\"}\n\n")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue