/*
* 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.web.servlet.view.xml;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
import com.fasterxml.jackson.databind.ser.BeanSerializerFactory;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.junit.Before;
import org.junit.Test;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.ScriptableObject;
import org.springframework.http.MediaType;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
import org.springframework.validation.BindingResult;
import org.springframework.web.servlet.View;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* @author Sebastien Deleuze
*/
public class MappingJackson2XmlViewTests {
private MappingJackson2XmlView view;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private Context jsContext;
private ScriptableObject jsScope;
@Before
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
jsContext = ContextFactory.getGlobal().enterContext();
jsScope = jsContext.initStandardObjects();
view = new MappingJackson2XmlView();
}
@Test
public void isExposePathVars() {
assertEquals("Must not expose path variables", false, view.isExposePathVariables());
}
@Test
public void renderSimpleMap() throws Exception {
Map<String, Object> model = new HashMap<>();
model.put("bindingResult", mock(BindingResult.class, "binding_result"));
model.put("foo", "bar");
view.setUpdateContentLength(true);
view.render(model, request, response);
assertEquals("no-store", response.getHeader("Cache-Control"));
assertEquals(MappingJackson2XmlView.DEFAULT_CONTENT_TYPE, response.getContentType());
String jsonResult = response.getContentAsString();
assertTrue(jsonResult.length() > 0);
assertEquals(jsonResult.length(), response.getContentLength());
validateResult();
}
@Test
public void renderWithSelectedContentType() throws Exception {
Map<String, Object> model = new HashMap<>();
model.put("foo", "bar");
view.render(model, request, response);
assertEquals("application/xml", response.getContentType());
request.setAttribute(View.SELECTED_CONTENT_TYPE, new MediaType("application", "vnd.example-v2+xml"));
view.render(model, request, response);
assertEquals("application/vnd.example-v2+xml", response.getContentType());
}
@Test
public void renderCaching() throws Exception {
view.setDisableCaching(false);
Map<String, Object> model = new HashMap<>();
model.put("bindingResult", mock(BindingResult.class, "binding_result"));
model.put("foo", "bar");
view.render(model, request, response);
assertNull(response.getHeader("Cache-Control"));
}
@Test
public void renderSimpleBean() throws Exception {
Object bean = new TestBeanSimple();
Map<String, Object> model = new HashMap<>();
model.put("bindingResult", mock(BindingResult.class, "binding_result"));
model.put("foo", bean);
view.setUpdateContentLength(true);
view.render(model, request, response);
assertTrue(response.getContentAsString().length() > 0);
assertEquals(response.getContentAsString().length(), response.getContentLength());
validateResult();
}
@Test
public void renderWithCustomSerializerLocatedByAnnotation() throws Exception {
Object bean = new TestBeanSimpleAnnotated();
Map<String, Object> model = new HashMap<>();
model.put("foo", bean);
view.render(model, request, response);
assertTrue(response.getContentAsString().length() > 0);
assertTrue(response.getContentAsString().contains("<testBeanSimple>custom</testBeanSimple>"));
validateResult();
}
@Test
public void renderWithCustomSerializerLocatedByFactory() throws Exception {
SerializerFactory factory = new DelegatingSerializerFactory(null);
XmlMapper mapper = new XmlMapper();
mapper.setSerializerFactory(factory);
view.setObjectMapper(mapper);
Object bean = new TestBeanSimple();
Map<String, Object> model = new HashMap<>();
model.put("foo", bean);
view.render(model, request, response);
String result = response.getContentAsString();
assertTrue(result.length() > 0);
assertTrue(result.contains("custom</testBeanSimple>"));
validateResult();
}
@Test
public void renderOnlySpecifiedModelKey() throws Exception {
view.setModelKey("bar");
Map<String, Object> model = new HashMap<>();
model.put("foo", "foo");
model.put("bar", "bar");
model.put("baz", "baz");
view.render(model, request, response);
String result = response.getContentAsString();
assertTrue(result.length() > 0);
assertFalse(result.contains("foo"));
assertTrue(result.contains("bar"));
assertFalse(result.contains("baz"));
validateResult();
}
@Test(expected = IllegalStateException.class)
public void renderModelWithMultipleKeys() throws Exception {
Map<String, Object> model = new TreeMap<>();
model.put("foo", "foo");
model.put("bar", "bar");
view.render(model, request, response);
fail();
}
@Test
public void renderSimpleBeanWithJsonView() throws Exception {
Object bean = new TestBeanSimple();
Map<String, Object> model = new HashMap<>();
model.put("bindingResult", mock(BindingResult.class, "binding_result"));
model.put("foo", bean);
model.put(JsonView.class.getName(), MyJacksonView1.class);
view.setUpdateContentLength(true);
view.render(model, request, response);
String content = response.getContentAsString();
assertTrue(content.length() > 0);
assertEquals(content.length(), response.getContentLength());
assertTrue(content.contains("foo"));
assertFalse(content.contains("boo"));
assertFalse(content.contains(JsonView.class.getName()));
}
private void validateResult() throws Exception {
Object xmlResult =
jsContext.evaluateString(jsScope, "(" + response.getContentAsString() + ")", "XML Stream", 1, null);
assertNotNull("XML Result did not eval as valid JavaScript", xmlResult);
assertEquals("application/xml", response.getContentType());
}
public interface MyJacksonView1 {
}
public interface MyJacksonView2 {
}
@SuppressWarnings("unused")
public static class TestBeanSimple {
@JsonView(MyJacksonView1.class)
private String property1 = "foo";
private boolean test = false;
@JsonView(MyJacksonView2.class)
private String property2 = "boo";
private TestChildBean child = new TestChildBean();
public String getProperty1() {
return property1;
}
public boolean getTest() {
return test;
}
public String getProperty2() {
return property2;
}
public Date getNow() {
return new Date();
}
public TestChildBean getChild() {
return child;
}
}
@JsonSerialize(using=TestBeanSimpleSerializer.class)
public static class TestBeanSimpleAnnotated extends TestBeanSimple {
}
public static class TestChildBean {
private String value = "bar";
private String baz = null;
private TestBeanSimple parent = null;
public String getValue() {
return value;
}
public String getBaz() {
return baz;
}
public TestBeanSimple getParent() {
return parent;
}
public void setParent(TestBeanSimple parent) {
this.parent = parent;
}
}
public static class TestBeanSimpleSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartObject();
jgen.writeFieldName("testBeanSimple");
jgen.writeString("custom");
jgen.writeEndObject();
}
}
@SuppressWarnings("serial")
public static class DelegatingSerializerFactory extends BeanSerializerFactory {
protected DelegatingSerializerFactory(SerializerFactoryConfig config) {
super(config);
}
@Override
public JsonSerializer<Object> createSerializer(SerializerProvider prov, JavaType type) throws JsonMappingException {
if (type.getRawClass() == TestBeanSimple.class) {
return new TestBeanSimpleSerializer();
}
else {
return super.createSerializer(prov, type);
}
}
}
}