String decoding for text only vs any MIME type

Follow-up to:
3d68c496f1

StringDecoder can be created in text-only vs "*/*" mode which in turn
allows a more intuitive order of client side decoders, e.g. SSE does
not have to be ahead of StringDecoder.

The commit also explicitly disables String from the supported types in
Jackson2Decoder leaving it to the StringDecoder in "*/*" mode which
comes after. This does not change the current arrangement since the
the StringDecoder ahead having "*/*" picks up JSON content just the
same.

From a broader perspective this change allows any decoder to deal with
String if it wants to after examining the content type be it the SSE
or another, custom decoder. For Jackson there is very little value in
decoding to String which works only if the output contains a single
JSON string but will fail to parse anything else (JSON object/array)
while StringDecoder in "*/*" mode will not fail.

Issue: SPR-15374
This commit is contained in:
Rossen Stoyanchev
2017-03-23 16:23:34 -04:00
parent a287e67992
commit 0662dbf044
17 changed files with 75 additions and 49 deletions

View File

@@ -22,6 +22,8 @@ import java.util.Map;
import static java.util.Arrays.asList;
import static java.util.Collections.*;
import org.junit.Test;
import static org.springframework.core.ResolvableType.forClass;
import static org.springframework.http.MediaType.*;
import static org.springframework.http.codec.json.Jackson2JsonDecoder.*;
import static org.springframework.http.codec.json.JacksonViewBean.*;
@@ -50,16 +52,18 @@ public class Jackson2JsonDecoderTests extends AbstractDataBufferAllocatingTestCa
@Test
public void canDecode() {
Jackson2JsonDecoder decoder = new Jackson2JsonDecoder();
ResolvableType type = ResolvableType.forClass(Pojo.class);
assertTrue(decoder.canDecode(type, APPLICATION_JSON));
assertTrue(decoder.canDecode(type, null));
assertFalse(decoder.canDecode(type, APPLICATION_XML));
assertTrue(decoder.canDecode(forClass(Pojo.class), APPLICATION_JSON));
assertTrue(decoder.canDecode(forClass(Pojo.class), null));
assertFalse(decoder.canDecode(forClass(String.class), null));
assertFalse(decoder.canDecode(forClass(Pojo.class), APPLICATION_XML));
}
@Test
public void decodePojo() throws Exception {
Flux<DataBuffer> source = Flux.just(stringBuffer("{\"foo\": \"foofoo\", \"bar\": \"barbar\"}"));
ResolvableType elementType = ResolvableType.forClass(Pojo.class);
ResolvableType elementType = forClass(Pojo.class);
Flux<Object> flux = new Jackson2JsonDecoder().decode(source, elementType, null,
emptyMap());
@@ -71,7 +75,7 @@ public class Jackson2JsonDecoderTests extends AbstractDataBufferAllocatingTestCa
@Test
public void decodePojoWithError() throws Exception {
Flux<DataBuffer> source = Flux.just(stringBuffer("{\"foo\":}"));
ResolvableType elementType = ResolvableType.forClass(Pojo.class);
ResolvableType elementType = forClass(Pojo.class);
Flux<Object> flux = new Jackson2JsonDecoder().decode(source, elementType, null,
emptyMap());
@@ -98,7 +102,7 @@ public class Jackson2JsonDecoderTests extends AbstractDataBufferAllocatingTestCa
Flux<DataBuffer> source = Flux.just(stringBuffer(
"[{\"bar\":\"b1\",\"foo\":\"f1\"},{\"bar\":\"b2\",\"foo\":\"f2\"}]"));
ResolvableType elementType = ResolvableType.forClass(Pojo.class);
ResolvableType elementType = forClass(Pojo.class);
Flux<Object> flux = new Jackson2JsonDecoder().decode(source, elementType, null,
emptyMap());
@@ -112,7 +116,7 @@ public class Jackson2JsonDecoderTests extends AbstractDataBufferAllocatingTestCa
public void fieldLevelJsonView() throws Exception {
Flux<DataBuffer> source = Flux.just(
stringBuffer("{\"withView1\" : \"with\", \"withView2\" : \"with\", \"withoutView\" : \"without\"}"));
ResolvableType elementType = ResolvableType.forClass(JacksonViewBean.class);
ResolvableType elementType = forClass(JacksonViewBean.class);
Map<String, Object> hints = singletonMap(JSON_VIEW_HINT, MyJacksonView1.class);
Flux<JacksonViewBean> flux = new Jackson2JsonDecoder()
.decode(source, elementType, null, hints).cast(JacksonViewBean.class);
@@ -130,7 +134,7 @@ public class Jackson2JsonDecoderTests extends AbstractDataBufferAllocatingTestCa
public void classLevelJsonView() throws Exception {
Flux<DataBuffer> source = Flux.just(stringBuffer(
"{\"withView1\" : \"with\", \"withView2\" : \"with\", \"withoutView\" : \"without\"}"));
ResolvableType elementType = ResolvableType.forClass(JacksonViewBean.class);
ResolvableType elementType = forClass(JacksonViewBean.class);
Map<String, Object> hints = singletonMap(JSON_VIEW_HINT, MyJacksonView3.class);
Flux<JacksonViewBean> flux = new Jackson2JsonDecoder()
.decode(source, elementType, null, hints).cast(JacksonViewBean.class);
@@ -147,7 +151,7 @@ public class Jackson2JsonDecoderTests extends AbstractDataBufferAllocatingTestCa
@Test
public void decodeEmptyBodyToMono() throws Exception {
Flux<DataBuffer> source = Flux.empty();
ResolvableType elementType = ResolvableType.forClass(Pojo.class);
ResolvableType elementType = forClass(Pojo.class);
Mono<Object> mono = new Jackson2JsonDecoder().decodeToMono(source, elementType,
null, emptyMap());