From 9e2d54feae131db85393a98953694d695ad9d73b Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Thu, 23 Mar 2017 10:16:12 -0700 Subject: [PATCH] Support json view with JacksonTester Closes gh-8672 --- .../json/ExampleJsonObjectWithView.java | 76 +++++++++++++++++++ .../json/JsonTestIntegrationTests.java | 14 ++++ .../test/json/AbstractJsonMarshalTester.java | 4 + .../boot/test/json/JacksonTester.java | 43 ++++++++++- .../boot/test/json/ExampleObject.java | 1 + .../boot/test/json/ExampleObjectWithView.java | 75 ++++++++++++++++++ .../json/JacksonTesterIntegrationTests.java | 53 ++++++++++++- 7 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleJsonObjectWithView.java create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObjectWithView.java diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleJsonObjectWithView.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleJsonObjectWithView.java new file mode 100644 index 00000000000..7bc564494d3 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/ExampleJsonObjectWithView.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2017 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.boot.test.autoconfigure.json; + +import com.fasterxml.jackson.annotation.JsonView; + +import org.springframework.util.ObjectUtils; + +/** + * Example object to read/write as JSON with view + * + * @author Madhura Bhave + */ +public class ExampleJsonObjectWithView { + + @JsonView(TestView.class) + private String value; + + private int id; + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + + public int getId() { + return this.id; + } + + public void setId(int id) { + this.id = id; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + ExampleJsonObjectWithView other = (ExampleJsonObjectWithView) obj; + return ObjectUtils.nullSafeEquals(this.value, other.value) + && ObjectUtils.nullSafeEquals(this.id, other.id); + } + + @Override + public String toString() { + return this.value + " " + this.id; + } + + static class TestView { + + } + +} + diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTestIntegrationTests.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTestIntegrationTests.java index dd408343c5d..0448e6c3929 100644 --- a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTestIntegrationTests.java +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/json/JsonTestIntegrationTests.java @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.json.BasicJsonTester; import org.springframework.boot.test.json.GsonTester; import org.springframework.boot.test.json.JacksonTester; +import org.springframework.boot.test.json.JsonContent; import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -42,6 +43,9 @@ public class JsonTestIntegrationTests { @Autowired private JacksonTester jacksonBasicJson; + @Autowired + private JacksonTester jacksonWithViewJson; + @Autowired private JacksonTester jacksonCustomJson; @@ -73,4 +77,14 @@ public class JsonTestIntegrationTests { assertThat(this.gsonJson.write(object)).isEqualToJson("example.json"); } + @Test + public void customView() throws Exception { + ExampleJsonObjectWithView object = new ExampleJsonObjectWithView(); + object.setValue("spring"); + JsonContent content = this.jacksonWithViewJson + .forView(ExampleJsonObjectWithView.TestView.class) + .write(object); + assertThat(content).doesNotHaveJsonPathValue("id"); + assertThat(content).isEqualToJson("example.json"); + } } diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java index e5aaf8b1e72..fbf56fa63c9 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/AbstractJsonMarshalTester.java @@ -113,6 +113,10 @@ public abstract class AbstractJsonMarshalTester { return this.type; } + protected final Class getResourceLoadClass() { + return this.resourceLoadClass; + } + /** * Return {@link JsonContent} from writing the specific value. * @param value the value to write diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java b/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java index 466d8e9d112..d72d9d81388 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/json/JacksonTester.java @@ -22,6 +22,8 @@ import java.io.Reader; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; import org.springframework.beans.factory.ObjectFactory; import org.springframework.core.ResolvableType; @@ -53,12 +55,15 @@ import org.springframework.util.Assert; * * @param the type under test * @author Phillip Webb + * @author Madhura Bhave * @since 1.4.0 */ public class JacksonTester extends AbstractJsonMarshalTester { private final ObjectMapper objectMapper; + private Class view; + /** * Create a new {@link JacksonTester} instance. * @param objectMapper the Jackson object mapper @@ -76,25 +81,47 @@ public class JacksonTester extends AbstractJsonMarshalTester { */ public JacksonTester(Class resourceLoadClass, ResolvableType type, ObjectMapper objectMapper) { + this(resourceLoadClass, type, objectMapper, null); + } + + public JacksonTester(Class resourceLoadClass, ResolvableType type, + ObjectMapper objectMapper, Class view) { super(resourceLoadClass, type); Assert.notNull(objectMapper, "ObjectMapper must not be null"); this.objectMapper = objectMapper; + this.view = view; } @Override protected T readObject(InputStream inputStream, ResolvableType type) throws IOException { - return this.objectMapper.readValue(inputStream, getType(type)); + return getObjectReader(type).readValue(inputStream); } @Override protected T readObject(Reader reader, ResolvableType type) throws IOException { - return this.objectMapper.readerFor(getType(type)).readValue(reader); + return getObjectReader(type).readValue(reader); + } + + private ObjectReader getObjectReader(ResolvableType type) { + ObjectReader objectReader = this.objectMapper.readerFor(getType(type)); + if (this.view != null) { + return objectReader.withView(this.view); + } + return objectReader; } @Override protected String writeObject(T value, ResolvableType type) throws IOException { - return this.objectMapper.writerFor(getType(type)).writeValueAsString(value); + return getObjectWriter(type).writeValueAsString(value); + } + + private ObjectWriter getObjectWriter(ResolvableType type) { + ObjectWriter objectWriter = this.objectMapper.writerFor(getType(type)); + if (this.view != null) { + return objectWriter.withView(this.view); + } + return objectWriter; } private JavaType getType(ResolvableType type) { @@ -124,6 +151,16 @@ public class JacksonTester extends AbstractJsonMarshalTester { new JacksonFieldInitializer().initFields(testInstance, objectMapperFactory); } + /** + * Returns a new instance of {@link JacksonTester} with the view + * that should be used for json serialization/deserialization. + * @param view the view class + * @return the new instance + */ + public JacksonTester forView(Class view) { + return new JacksonTester(this.getResourceLoadClass(), this.getType(), this.objectMapper, view); + } + /** * {@link FieldInitializer} for Jackson. */ diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObject.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObject.java index 9ad5a00d49f..5c8d45e18b0 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObject.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObject.java @@ -64,3 +64,4 @@ public class ExampleObject { } } + diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObjectWithView.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObjectWithView.java new file mode 100644 index 00000000000..1bd501c0944 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/ExampleObjectWithView.java @@ -0,0 +1,75 @@ +/* + * Copyright 2012-2017 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.boot.test.json; + +import com.fasterxml.jackson.annotation.JsonView; + +import org.springframework.util.ObjectUtils; + +/** + * Example object used for serialization/deserialization with view. + * + * @author Madhura Bhave + */ +public class ExampleObjectWithView { + + @JsonView(TestView.class) + private String name; + + private int age; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return this.age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + ExampleObjectWithView other = (ExampleObjectWithView) obj; + return ObjectUtils.nullSafeEquals(this.name, other.name) + && ObjectUtils.nullSafeEquals(this.age, other.age); + } + + @Override + public String toString() { + return this.name + " " + this.age; + } + + static class TestView { + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java index f85532cad2d..74c693c2bc7 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/json/JacksonTesterIntegrationTests.java @@ -16,14 +16,19 @@ package org.springframework.boot.test.json; +import java.io.Reader; +import java.io.StringReader; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Before; import org.junit.Test; +import org.springframework.core.io.ByteArrayResource; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -33,18 +38,27 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class JacksonTesterIntegrationTests { + private JacksonTester simpleJson; + + private JacksonTester jsonWithView; + private JacksonTester> listJson; private JacksonTester> mapJson; + private ObjectMapper objectMapper; + + private static final String JSON = "{\"name\":\"Spring\",\"age\":123}"; + @Before public void setup() { - JacksonTester.initFields(this, new ObjectMapper()); + this.objectMapper = new ObjectMapper(); + JacksonTester.initFields(this, this.objectMapper); } @Test public void typicalListTest() throws Exception { - String example = "[{\"name\":\"Spring\",\"age\":123}]"; + String example = "[" + JSON + "]"; assertThat(this.listJson.parse(example)).asList().hasSize(1); assertThat(this.listJson.parse(example).getObject().get(0).getName()) .isEqualTo("Spring"); @@ -59,4 +73,39 @@ public class JacksonTesterIntegrationTests { .isEqualTo(1); } + @Test + public void writeWithView() throws Exception { + this.objectMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION); + ExampleObjectWithView object = new ExampleObjectWithView(); + object.setName("Spring"); + object.setAge(123); + JsonContent content = this.jsonWithView.forView( + ExampleObjectWithView.TestView.class).write(object); + assertThat(content).extractingJsonPathStringValue("@.name") + .isEqualTo("Spring"); + assertThat(content).doesNotHaveJsonPathValue("age"); + } + + @Test + public void readWithResourceAndView() throws Exception { + this.objectMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION); + ByteArrayResource resource = new ByteArrayResource(JSON.getBytes()); + ObjectContent content = this.jsonWithView.forView( + ExampleObjectWithView.TestView.class).read(resource); + assertThat(content.getObject().getName()) + .isEqualTo("Spring"); + assertThat(content.getObject().getAge()).isEqualTo(0); + } + + @Test + public void readWithReaderAndView() throws Exception { + this.objectMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION); + Reader reader = new StringReader(JSON); + ObjectContent content = this.jsonWithView.forView( + ExampleObjectWithView.TestView.class).read(reader); + assertThat(content.getObject().getName()) + .isEqualTo("Spring"); + assertThat(content.getObject().getAge()).isEqualTo(0); + } + }