Add support for Jackson serialization views
Spring MVC now supports Jackon's serialization views for rendering different subsets of the same POJO from different controller methods (e.g. detailed page vs summary view). Issue: SPR-7156
This commit is contained in:
committed by
Rossen Stoyanchev
parent
673a497923
commit
be0b69cbf1
@@ -90,6 +90,7 @@ public class Jackson2ObjectMapperFactoryBeanTests {
|
||||
public void testBooleanSetters() {
|
||||
this.factory.setAutoDetectFields(false);
|
||||
this.factory.setAutoDetectGettersSetters(false);
|
||||
this.factory.setDefaultViewInclusion(false);
|
||||
this.factory.setFailOnEmptyBeans(false);
|
||||
this.factory.setIndentOutput(true);
|
||||
this.factory.afterPropertiesSet();
|
||||
@@ -100,6 +101,7 @@ public class Jackson2ObjectMapperFactoryBeanTests {
|
||||
assertFalse(objectMapper.getDeserializationConfig().isEnabled(MapperFeature.AUTO_DETECT_FIELDS));
|
||||
assertFalse(objectMapper.getSerializationConfig().isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
|
||||
assertFalse(objectMapper.getDeserializationConfig().isEnabled(MapperFeature.AUTO_DETECT_SETTERS));
|
||||
assertFalse(objectMapper.getDeserializationConfig().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION));
|
||||
assertFalse(objectMapper.getSerializationConfig().isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS));
|
||||
assertTrue(objectMapper.getSerializationConfig().isEnabled(SerializationFeature.INDENT_OUTPUT));
|
||||
assertTrue(objectMapper.getSerializationConfig().getSerializationInclusion() == JsonInclude.Include.ALWAYS);
|
||||
@@ -253,6 +255,7 @@ public class Jackson2ObjectMapperFactoryBeanTests {
|
||||
assertTrue(objectMapper.getFactory().isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS));
|
||||
|
||||
assertFalse(objectMapper.getSerializationConfig().isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
|
||||
assertTrue(objectMapper.getDeserializationConfig().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION));
|
||||
assertFalse(objectMapper.getDeserializationConfig().isEnabled(MapperFeature.AUTO_DETECT_FIELDS));
|
||||
assertFalse(objectMapper.getFactory().isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
|
||||
assertFalse(objectMapper.getFactory().isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES));
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
@@ -237,6 +238,22 @@ public class MappingJackson2HttpMessageConverterTests {
|
||||
assertEquals(")]}',\"foo\"", outputMessage.getBodyAsString(Charset.forName("UTF-8")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonView() throws Exception {
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
JacksonViewBean bean = new JacksonViewBean();
|
||||
bean.setWithView1("with");
|
||||
bean.setWithView2("with");
|
||||
bean.setWithoutView("without");
|
||||
MappingJacksonValueHolder jsv = new MappingJacksonValueHolder(bean, MyJacksonView1.class);
|
||||
this.converter.writeInternal(jsv, outputMessage);
|
||||
|
||||
String result = outputMessage.getBodyAsString(Charset.forName("UTF-8"));
|
||||
assertTrue(result.contains("\"withView1\":\"with\""));
|
||||
assertFalse(result.contains("\"withView2\":\"with\""));
|
||||
assertTrue(result.contains("\"withoutView\":\"without\""));
|
||||
}
|
||||
|
||||
|
||||
public static class MyBean {
|
||||
|
||||
@@ -315,4 +332,42 @@ public class MappingJackson2HttpMessageConverterTests {
|
||||
}
|
||||
}
|
||||
|
||||
private interface MyJacksonView1 {};
|
||||
private interface MyJacksonView2 {};
|
||||
|
||||
private static class JacksonViewBean {
|
||||
|
||||
@JsonView(MyJacksonView1.class)
|
||||
private String withView1;
|
||||
|
||||
@JsonView(MyJacksonView2.class)
|
||||
private String withView2;
|
||||
|
||||
private String withoutView;
|
||||
|
||||
public String getWithView1() {
|
||||
return withView1;
|
||||
}
|
||||
|
||||
public void setWithView1(String withView1) {
|
||||
this.withView1 = withView1;
|
||||
}
|
||||
|
||||
public String getWithView2() {
|
||||
return withView2;
|
||||
}
|
||||
|
||||
public void setWithView2(String withView2) {
|
||||
this.withView2 = withView2;
|
||||
}
|
||||
|
||||
public String getWithoutView() {
|
||||
return withoutView;
|
||||
}
|
||||
|
||||
public void setWithoutView(String withoutView) {
|
||||
this.withoutView = withoutView;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ public class AbstractJettyServerTestCase {
|
||||
|
||||
protected static String baseUrl;
|
||||
|
||||
protected static MediaType contentType;
|
||||
protected static MediaType textContentType;
|
||||
protected static MediaType jsonContentType;
|
||||
|
||||
private static Server jettyServer;
|
||||
|
||||
@@ -67,14 +68,19 @@ public class AbstractJettyServerTestCase {
|
||||
baseUrl = "http://localhost:" + port;
|
||||
ServletContextHandler handler = new ServletContextHandler();
|
||||
byte[] bytes = helloWorld.getBytes("UTF-8");
|
||||
contentType = new MediaType("text", "plain", Collections
|
||||
textContentType = new MediaType("text", "plain", Collections
|
||||
.singletonMap("charset", "UTF-8"));
|
||||
handler.addServlet(new ServletHolder(new GetServlet(bytes, contentType)), "/get");
|
||||
handler.addServlet(new ServletHolder(new GetServlet(new byte[0], contentType)), "/get/nothing");
|
||||
jsonContentType = new MediaType("application", "json", Collections
|
||||
.singletonMap("charset", "UTF-8"));
|
||||
handler.addServlet(new ServletHolder(new GetServlet(bytes, textContentType)), "/get");
|
||||
handler.addServlet(new ServletHolder(new GetServlet(new byte[0], textContentType)), "/get/nothing");
|
||||
handler.addServlet(new ServletHolder(new GetServlet(bytes, null)), "/get/nocontenttype");
|
||||
handler.addServlet(
|
||||
new ServletHolder(new PostServlet(helloWorld, baseUrl + "/post/1", bytes, contentType)),
|
||||
new ServletHolder(new PostServlet(helloWorld, baseUrl + "/post/1", bytes, textContentType)),
|
||||
"/post");
|
||||
handler.addServlet(
|
||||
new ServletHolder(new JsonPostServlet(baseUrl + "/jsonpost/1", jsonContentType)),
|
||||
"/jsonpost");
|
||||
handler.addServlet(new ServletHolder(new StatusCodeServlet(204)), "/status/nocontent");
|
||||
handler.addServlet(new ServletHolder(new StatusCodeServlet(304)), "/status/notmodified");
|
||||
handler.addServlet(new ServletHolder(new ErrorServlet(404)), "/status/notfound");
|
||||
@@ -83,7 +89,7 @@ public class AbstractJettyServerTestCase {
|
||||
handler.addServlet(new ServletHolder(new MultipartServlet()), "/multipart");
|
||||
handler.addServlet(new ServletHolder(new DeleteServlet()), "/delete");
|
||||
handler.addServlet(
|
||||
new ServletHolder(new PutServlet(helloWorld, bytes, contentType)),
|
||||
new ServletHolder(new PutServlet(helloWorld, bytes, textContentType)),
|
||||
"/put");
|
||||
jettyServer.setHandler(handler);
|
||||
jettyServer.start();
|
||||
@@ -185,6 +191,33 @@ public class AbstractJettyServerTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class JsonPostServlet extends HttpServlet {
|
||||
|
||||
private final String location;
|
||||
|
||||
private final MediaType contentType;
|
||||
|
||||
private JsonPostServlet(String location, MediaType contentType) {
|
||||
this.location = location;
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
assertTrue("Invalid request content-length", request.getContentLength() > 0);
|
||||
assertNotNull("No content-type", request.getContentType());
|
||||
String body = FileCopyUtils.copyToString(request.getReader());
|
||||
response.setStatus(HttpServletResponse.SC_CREATED);
|
||||
response.setHeader("Location", location);
|
||||
response.setContentType(contentType.toString());
|
||||
byte[] bytes = body.getBytes("UTF-8");
|
||||
response.setContentLength(bytes.length);;
|
||||
FileCopyUtils.copy(bytes, response.getOutputStream());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class PutServlet extends HttpServlet {
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
|
||||
ResponseEntity<String> entity = futureEntity.get();
|
||||
assertEquals("Invalid content", helloWorld, entity.getBody());
|
||||
assertFalse("No headers", entity.getHeaders().isEmpty());
|
||||
assertEquals("Invalid content-type", contentType, entity.getHeaders().getContentType());
|
||||
assertEquals("Invalid content-type", textContentType, entity.getHeaders().getContentType());
|
||||
assertEquals("Invalid status code", HttpStatus.OK, entity.getStatusCode());
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa
|
||||
public void onSuccess(ResponseEntity<String> entity) {
|
||||
assertEquals("Invalid content", helloWorld, entity.getBody());
|
||||
assertFalse("No headers", entity.getHeaders().isEmpty());
|
||||
assertEquals("Invalid content-type", contentType, entity.getHeaders().getContentType());
|
||||
assertEquals("Invalid content-type", textContentType, entity.getHeaders().getContentType());
|
||||
assertEquals("Invalid status code", HttpStatus.OK, entity.getStatusCode());
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.nio.charset.Charset;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -35,6 +36,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.json.MappingJacksonValueHolder;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
@@ -63,7 +65,7 @@ public class RestTemplateIntegrationTests extends AbstractJettyServerTestCase {
|
||||
ResponseEntity<String> entity = template.getForEntity(baseUrl + "/{method}", String.class, "get");
|
||||
assertEquals("Invalid content", helloWorld, entity.getBody());
|
||||
assertFalse("No headers", entity.getHeaders().isEmpty());
|
||||
assertEquals("Invalid content-type", contentType, entity.getHeaders().getContentType());
|
||||
assertEquals("Invalid content-type", textContentType, entity.getHeaders().getContentType());
|
||||
assertEquals("Invalid status code", HttpStatus.OK, entity.getStatusCode());
|
||||
}
|
||||
|
||||
@@ -198,4 +200,79 @@ public class RestTemplateIntegrationTests extends AbstractJettyServerTestCase {
|
||||
assertFalse(result.hasBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonPostForObject() throws URISyntaxException {
|
||||
HttpHeaders entityHeaders = new HttpHeaders();
|
||||
entityHeaders.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));
|
||||
MySampleBean bean = new MySampleBean();
|
||||
bean.setWith1("with");
|
||||
bean.setWith2("with");
|
||||
bean.setWithout("without");
|
||||
HttpEntity<MySampleBean> entity = new HttpEntity<MySampleBean>(bean, entityHeaders);
|
||||
String s = template.postForObject(baseUrl + "/jsonpost", entity, String.class, "post");
|
||||
assertTrue(s.contains("\"with1\":\"with\""));
|
||||
assertTrue(s.contains("\"with2\":\"with\""));
|
||||
assertTrue(s.contains("\"without\":\"without\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonPostForObjectWithJacksonView() throws URISyntaxException {
|
||||
HttpHeaders entityHeaders = new HttpHeaders();
|
||||
entityHeaders.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));
|
||||
MySampleBean bean = new MySampleBean("with", "with", "without");
|
||||
MappingJacksonValueHolder jsv = new MappingJacksonValueHolder(bean, MyJacksonView1.class);
|
||||
HttpEntity<MappingJacksonValueHolder> entity = new HttpEntity<MappingJacksonValueHolder>(jsv);
|
||||
String s = template.postForObject(baseUrl + "/jsonpost", entity, String.class, "post");
|
||||
assertTrue(s.contains("\"with1\":\"with\""));
|
||||
assertFalse(s.contains("\"with2\":\"with\""));
|
||||
assertTrue(s.contains("\"without\":\"without\""));
|
||||
}
|
||||
|
||||
public interface MyJacksonView1 {};
|
||||
public interface MyJacksonView2 {};
|
||||
|
||||
public static class MySampleBean {
|
||||
|
||||
@JsonView(MyJacksonView1.class)
|
||||
private String with1;
|
||||
|
||||
@JsonView(MyJacksonView2.class)
|
||||
private String with2;
|
||||
|
||||
private String without;
|
||||
|
||||
private MySampleBean() {
|
||||
}
|
||||
|
||||
private MySampleBean(String with1, String with2, String without) {
|
||||
this.with1 = with1;
|
||||
this.with2 = with2;
|
||||
this.without = without;
|
||||
}
|
||||
|
||||
public String getWith1() {
|
||||
return with1;
|
||||
}
|
||||
|
||||
public void setWith1(String with1) {
|
||||
this.with1 = with1;
|
||||
}
|
||||
|
||||
public String getWith2() {
|
||||
return with2;
|
||||
}
|
||||
|
||||
public void setWith2(String with2) {
|
||||
this.with2 = with2;
|
||||
}
|
||||
|
||||
public String getWithout() {
|
||||
return without;
|
||||
}
|
||||
|
||||
public void setWithout(String without) {
|
||||
this.without = without;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user