/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.http.codec.json; import java.util.List; import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Test; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import org.springframework.core.ResolvableType; import org.springframework.core.codec.CodecException; import org.springframework.core.codec.DecodingException; import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.codec.Pojo; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.springframework.core.ResolvableType.forClass; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.http.MediaType.APPLICATION_XML; import static org.springframework.http.codec.json.Jackson2JsonDecoder.JSON_VIEW_HINT; import static org.springframework.http.codec.json.JacksonViewBean.MyJacksonView1; import static org.springframework.http.codec.json.JacksonViewBean.MyJacksonView3; /** * Unit tests for {@link Jackson2JsonDecoder}. * * @author Sebastien Deleuze * @author Rossen Stoyanchev */ public class Jackson2JsonDecoderTests extends AbstractDataBufferAllocatingTestCase { @Test public void canDecode() { Jackson2JsonDecoder decoder = new Jackson2JsonDecoder(); 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 = forClass(Pojo.class); Flux<Object> flux = new Jackson2JsonDecoder().decode(source, elementType, null, emptyMap()); StepVerifier.create(flux) .expectNext(new Pojo("foofoo", "barbar")) .verifyComplete(); } @Test public void decodePojoWithError() throws Exception { Flux<DataBuffer> source = Flux.just(stringBuffer("{\"foo\":}")); ResolvableType elementType = forClass(Pojo.class); Flux<Object> flux = new Jackson2JsonDecoder().decode(source, elementType, null, emptyMap()); StepVerifier.create(flux).verifyError(CodecException.class); } @Test public void decodeToList() throws Exception { Flux<DataBuffer> source = Flux.just(stringBuffer( "[{\"bar\":\"b1\",\"foo\":\"f1\"},{\"bar\":\"b2\",\"foo\":\"f2\"}]")); ResolvableType elementType = ResolvableType.forClassWithGenerics(List.class, Pojo.class); Mono<Object> mono = new Jackson2JsonDecoder().decodeToMono(source, elementType, null, emptyMap()); StepVerifier.create(mono) .expectNext(asList(new Pojo("f1", "b1"), new Pojo("f2", "b2"))) .expectComplete() .verify(); } @Test public void decodeToFlux() throws Exception { Flux<DataBuffer> source = Flux.just(stringBuffer( "[{\"bar\":\"b1\",\"foo\":\"f1\"},{\"bar\":\"b2\",\"foo\":\"f2\"}]")); ResolvableType elementType = forClass(Pojo.class); Flux<Object> flux = new Jackson2JsonDecoder().decode(source, elementType, null, emptyMap()); StepVerifier.create(flux) .expectNext(new Pojo("f1", "b1")) .expectNext(new Pojo("f2", "b2")) .verifyComplete(); } @Test public void fieldLevelJsonView() throws Exception { Flux<DataBuffer> source = Flux.just( stringBuffer("{\"withView1\" : \"with\", \"withView2\" : \"with\", \"withoutView\" : \"without\"}")); 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); StepVerifier.create(flux) .consumeNextWith(b -> { assertTrue(b.getWithView1().equals("with")); assertNull(b.getWithView2()); assertNull(b.getWithoutView()); }) .verifyComplete(); } @Test public void classLevelJsonView() throws Exception { Flux<DataBuffer> source = Flux.just(stringBuffer( "{\"withView1\" : \"with\", \"withView2\" : \"with\", \"withoutView\" : \"without\"}")); 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); StepVerifier.create(flux) .consumeNextWith(b -> { assertNull(b.getWithView1()); assertNull(b.getWithView2()); assertTrue(b.getWithoutView().equals("without")); }) .verifyComplete(); } @Test public void decodeEmptyBodyToMono() throws Exception { Flux<DataBuffer> source = Flux.empty(); ResolvableType elementType = forClass(Pojo.class); Mono<Object> mono = new Jackson2JsonDecoder().decodeToMono(source, elementType, null, emptyMap()); StepVerifier.create(mono) .expectNextCount(0) .verifyComplete(); } @Test public void invalidData() throws Exception { Flux<DataBuffer> source = Flux.just(stringBuffer( "{\"foofoo\": \"foofoo\", \"barbar\": \"barbar\"}")); ResolvableType elementType = forClass(Pojo.class); Flux<Object> flux = new Jackson2JsonDecoder(new ObjectMapper()).decode(source, elementType, null, emptyMap()); StepVerifier.create(flux).verifyErrorMatches(ex -> ex instanceof DecodingException); } @Test public void noDefaultConstructor() throws Exception { Flux<DataBuffer> source = Flux.just(stringBuffer( "{\"property1\":\"foo\",\"property2\":\"bar\"}")); ResolvableType elementType = forClass(BeanWithNoDefaultConstructor.class); Flux<Object> flux = new Jackson2JsonDecoder().decode(source, elementType, null, emptyMap()); StepVerifier.create(flux).verifyError(CodecException.class); } private static class BeanWithNoDefaultConstructor { private final String property1; private final String property2; public BeanWithNoDefaultConstructor(String property1, String property2) { this.property1 = property1; this.property2 = property2; } public String getProperty1() { return property1; } public String getProperty2() { return property2; } } }