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