提交 1bd84db5 编写于 作者: J Juergen Hoeller

polishing (alignment with 3.1 GA)

上级 6316a459
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 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.
......@@ -57,10 +57,11 @@ public class MarshallingMessageConverter implements MessageConverter, Initializi
private MessageType targetType = MessageType.BYTES;
/**
* Construct a new <code>MarshallingMessageConverter</code> with no {@link Marshaller} or {@link Unmarshaller} set.
* The marshaller must be set after construction by invoking {@link #setMarshaller(Marshaller)} and
* {@link #setUnmarshaller(Unmarshaller)} .
* Construct a new <code>MarshallingMessageConverter</code> with no {@link Marshaller}
* or {@link Unmarshaller} set. The marshaller must be set after construction by invoking
* {@link #setMarshaller(Marshaller)} and {@link #setUnmarshaller(Unmarshaller)} .
*/
public MarshallingMessageConverter() {
}
......@@ -80,7 +81,7 @@ public class MarshallingMessageConverter implements MessageConverter, Initializi
if (!(marshaller instanceof Unmarshaller)) {
throw new IllegalArgumentException(
"Marshaller [" + marshaller + "] does not implement the Unmarshaller " +
"interface. Please set an Unmarshaller explicitely by using the " +
"interface. Please set an Unmarshaller explicitly by using the " +
"MarshallingMessageConverter(Marshaller, Unmarshaller) constructor.");
}
else {
......@@ -127,6 +128,7 @@ public class MarshallingMessageConverter implements MessageConverter, Initializi
* @see MessageType#TEXT
*/
public void setTargetType(MessageType targetType) {
Assert.notNull(targetType, "MessageType must not be null");
this.targetType = targetType;
}
......@@ -251,8 +253,8 @@ public class MarshallingMessageConverter implements MessageConverter, Initializi
protected Message marshalToMessage(Object object, Session session, Marshaller marshaller, MessageType targetType)
throws JMSException, IOException, XmlMappingException {
throw new IllegalArgumentException(
"Unsupported message type [" + targetType + "]. Cannot marshal to the specified message type.");
throw new IllegalArgumentException("Unsupported message type [" + targetType +
"]. MarshallingMessageConverter by default only supports TextMessages and BytesMessages.");
}
......@@ -308,8 +310,8 @@ public class MarshallingMessageConverter implements MessageConverter, Initializi
protected Object unmarshalFromMessage(Message message, Unmarshaller unmarshaller)
throws JMSException, IOException, XmlMappingException {
throw new IllegalArgumentException(
"MarshallingMessageConverter only supports TextMessages and BytesMessages");
throw new IllegalArgumentException("Unsupported message type [" + message.getClass() +
"]. MarshallingMessageConverter by default only supports TextMessages and BytesMessages.");
}
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 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.
......@@ -110,11 +110,12 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
private ClassLoader classLoader;
/**
* Returns the XStream instance used by this marshaller.
*/
public XStream getXStream() {
return xstream;
return this.xstream;
}
/**
......@@ -150,7 +151,6 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
/**
* Sets an alias/type map, consisting of string aliases mapped to classes. Keys are aliases; values are either
* {@code Class} instances, or String class names.
*
* @see XStream#alias(String, Class)
*/
public void setAliases(Map<String, ?> aliases) throws ClassNotFoundException {
......@@ -165,7 +165,6 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
* Sets the aliases by type map, consisting of string aliases mapped to classes. Any class that is assignable to
* this type will be aliased to the same name. Keys are aliases; values are either
* {@code Class} instances, or String class names.
*
* @see XStream#aliasType(String, Class)
*/
public void setAliasesByType(Map<String, ?> aliases) throws ClassNotFoundException {
......@@ -325,9 +324,9 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
}
/**
* Set the auto-detection mode of XStream.
* <p><strong>Note</strong> that auto-detection implies that the XStream is configured while it is processing the
* XML steams, and thus introduces a potential concurrency problem.
* Set the autodetection mode of XStream.
* <p><strong>Note</strong> that auto-detection implies that the XStream is configured while
* it is processing the XML streams, and thus introduces a potential concurrency problem.
* @see XStream#autodetectAnnotations(boolean)
*/
public void setAutodetectAnnotations(boolean autodetectAnnotations) {
......@@ -358,22 +357,24 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
this.supportedClasses = supportedClasses;
}
public final void afterPropertiesSet() throws Exception {
customizeXStream(getXStream());
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public final void afterPropertiesSet() throws Exception {
customizeXStream(getXStream());
}
/**
* Template to allow for customizing of the given {@link XStream}.
* <p>Default implementation is empty.
* <p>The default implementation is empty.
* @param xstream the {@code XStream} instance
*/
protected void customizeXStream(XStream xstream) {
}
public boolean supports(Class clazz) {
if (ObjectUtils.isEmpty(this.supportedClasses)) {
return true;
......@@ -438,8 +439,8 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
@Override
protected void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException {
if (streamDriver != null) {
marshal(graph, streamDriver.createWriter(writer));
if (this.streamDriver != null) {
marshal(graph, this.streamDriver.createWriter(writer));
}
else {
marshal(graph, new CompactWriter(writer));
......@@ -467,6 +468,7 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
}
}
// Unmarshalling
@Override
......
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 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.
......@@ -34,17 +34,17 @@ import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.AbstractView;
/**
* Spring-MVC {@link View} that renders JSON content by serializing the model for the current request using <a
* href="http://jackson.codehaus.org/">Jackson's</a> {@link ObjectMapper}.
* Spring MVC {@link View} that renders JSON content by serializing the model for the current request
* using <a href="http://jackson.codehaus.org/">Jackson's</a> {@link ObjectMapper}.
*
* <p>By default, the entire contents of the model map (with the exception of framework-specific classes) will be
* encoded as JSON. For cases where the contents of the map need to be filtered, users may specify a specific set of
* model attributes to encode via the {@link #setRenderedAttributes(Set) renderedAttributes} property.
* <p>By default, the entire contents of the model map (with the exception of framework-specific classes)
* will be encoded as JSON. If the model contains only one key, you can have it extracted encoded as JSON
* alone via {@link #setExtractValueFromSingleKeyModel}.
*
* @author Jeremy Grelle
* @author Arjen Poutsma
* @see org.springframework.http.converter.json.MappingJacksonHttpMessageConverter
* @since 3.0
* @see org.springframework.http.converter.json.MappingJacksonHttpMessageConverter
*/
public class MappingJacksonJsonView extends AbstractView {
......@@ -53,6 +53,7 @@ public class MappingJacksonJsonView extends AbstractView {
*/
public static final String DEFAULT_CONTENT_TYPE = "application/json";
private ObjectMapper objectMapper = new ObjectMapper();
private JsonEncoding encoding = JsonEncoding.UTF8;
......@@ -63,6 +64,7 @@ public class MappingJacksonJsonView extends AbstractView {
private boolean disableCaching = true;
/**
* Construct a new {@code JacksonJsonView}, setting the content type to {@code application/json}.
*/
......@@ -70,14 +72,15 @@ public class MappingJacksonJsonView extends AbstractView {
setContentType(DEFAULT_CONTENT_TYPE);
}
/**
* Sets the {@code ObjectMapper} for this view. If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper}
* is used.
*
* <p>Setting a custom-configured {@code ObjectMapper} is one way to take further control of the JSON serialization
* process. For example, an extended {@link SerializerFactory} can be configured that provides custom serializers for
* specific types. The other option for refining the serialization process is to use Jackson's provided annotations on
* the types to be serialized, in which case a custom-configured ObjectMapper is unnecessary.
* Sets the {@code ObjectMapper} for this view.
* If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} is used.
* <p>Setting a custom-configured {@code ObjectMapper} is one way to take further control
* of the JSON serialization process. For example, an extended {@link SerializerFactory}
* can be configured that provides custom serializers for specific types. The other option
* for refining the serialization process is to use Jackson's provided annotations on the
* types to be serialized, in which case a custom-configured ObjectMapper is unnecessary.
*/
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "'objectMapper' must not be null");
......@@ -85,7 +88,8 @@ public class MappingJacksonJsonView extends AbstractView {
}
/**
* Sets the {@code JsonEncoding} for this converter. By default, {@linkplain JsonEncoding#UTF8 UTF-8} is used.
* Set the {@code JsonEncoding} for this converter.
* By default, {@linkplain JsonEncoding#UTF8 UTF-8} is used.
*/
public void setEncoding(JsonEncoding encoding) {
Assert.notNull(encoding, "'encoding' must not be null");
......@@ -93,45 +97,46 @@ public class MappingJacksonJsonView extends AbstractView {
}
/**
* Indicates whether the JSON output by this view should be prefixed with "{@code {} &&}". Default is false.
*
* <p> Prefixing the JSON string in this manner is used to help prevent JSON Hijacking. The prefix renders the string
* syntactically invalid as a script so that it cannot be hijacked. This prefix does not affect the evaluation of JSON,
* but if JSON validation is performed on the string, the prefix would need to be ignored.
* Indicates whether the JSON output by this view should be prefixed with <tt>"{} && "</tt>.
* Default is false.
* <p>Prefixing the JSON string in this manner is used to help prevent JSON Hijacking.
* The prefix renders the string syntactically invalid as a script so that it cannot be hijacked.
* This prefix does not affect the evaluation of JSON, but if JSON validation is performed
* on the string, the prefix would need to be ignored.
*/
public void setPrefixJson(boolean prefixJson) {
this.prefixJson = prefixJson;
}
/**
* Returns the attributes in the model that should be rendered by this view.
* Set the attributes in the model that should be rendered by this view.
* When set, all other model attributes will be ignored.
*/
public Set<String> getRenderedAttributes() {
return renderedAttributes;
public void setRenderedAttributes(Set<String> renderedAttributes) {
this.renderedAttributes = renderedAttributes;
}
/**
* Sets the attributes in the model that should be rendered by this view. When set, all other model attributes will be
* ignored.
* Return the attributes in the model that should be rendered by this view.
*/
public void setRenderedAttributes(Set<String> renderedAttributes) {
this.renderedAttributes = renderedAttributes;
public Set<String> getRenderedAttributes() {
return this.renderedAttributes;
}
/**
* Disables caching of the generated JSON.
*
* <p>Default is {@code true}, which will prevent the client from caching the generated JSON.
*/
public void setDisableCaching(boolean disableCaching) {
this.disableCaching = disableCaching;
}
@Override
protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {
response.setContentType(getContentType());
response.setCharacterEncoding(encoding.getJavaName());
if (disableCaching) {
response.setCharacterEncoding(this.encoding.getJavaName());
if (this.disableCaching) {
response.addHeader("Pragma", "no-cache");
response.addHeader("Cache-Control", "no-cache, no-store, max-age=0");
response.addDateHeader("Expires", 1L);
......@@ -139,32 +144,30 @@ public class MappingJacksonJsonView extends AbstractView {
}
@Override
protected void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request,
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
Object value = filterModel(model);
JsonGenerator generator =
objectMapper.getJsonFactory().createJsonGenerator(response.getOutputStream(), encoding);
if (prefixJson) {
this.objectMapper.getJsonFactory().createJsonGenerator(response.getOutputStream(), this.encoding);
if (this.prefixJson) {
generator.writeRaw("{} && ");
}
objectMapper.writeValue(generator, value);
this.objectMapper.writeValue(generator, value);
}
/**
* Filters out undesired attributes from the given model. The return value can be either another {@link Map}, or a
* single value object.
*
* <p>Default implementation removes {@link BindingResult} instances and entries not included in the {@link
* #setRenderedAttributes(Set) renderedAttributes} property.
*
* Filters out undesired attributes from the given model.
* The return value can be either another {@link Map} or a single value object.
* <p>The default implementation removes {@link BindingResult} instances and entries
* not included in the {@link #setRenderedAttributes renderedAttributes} property.
* @param model the model, as passed on to {@link #renderMergedOutputModel}
* @return the object to be rendered
*/
protected Object filterModel(Map<String, Object> model) {
Map<String, Object> result = new HashMap<String, Object>(model.size());
Set<String> renderedAttributes =
!CollectionUtils.isEmpty(this.renderedAttributes) ? this.renderedAttributes : model.keySet();
(!CollectionUtils.isEmpty(this.renderedAttributes) ? this.renderedAttributes : model.keySet());
for (Map.Entry<String, Object> entry : model.entrySet()) {
if (!(entry.getValue() instanceof BindingResult) && renderedAttributes.contains(entry.getKey())) {
result.put(entry.getKey(), entry.getValue());
......
......@@ -16,16 +16,13 @@
package org.springframework.http.converter.json;
import java.io.EOFException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.type.JavaType;
......@@ -55,6 +52,7 @@ public class MappingJacksonHttpMessageConverter extends AbstractHttpMessageConve
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private ObjectMapper objectMapper = new ObjectMapper();
private boolean prefixJson = false;
......@@ -106,27 +104,6 @@ public class MappingJacksonHttpMessageConverter extends AbstractHttpMessageConve
return (this.objectMapper.canDeserialize(javaType) && canRead(mediaType));
}
/**
* Returns the Jackson {@link JavaType} for the specific class.
* <p>The default implementation returns {@link TypeFactory#type(java.lang.reflect.Type)},
* but this can be overridden in subclasses, to allow for custom generic collection handling.
* For instance:
* <pre class="code">
* protected JavaType getJavaType(Class&lt;?&gt; clazz) {
* if (List.class.isAssignableFrom(clazz)) {
* return TypeFactory.collectionType(ArrayList.class, MyBean.class);
* } else {
* return super.getJavaType(clazz);
* }
* }
* </pre>
* @param clazz the class to return the java type for
* @return the java type
*/
protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.type(clazz);
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return (this.objectMapper.canSerialize(clazz) && canWrite(mediaType));
......@@ -146,36 +123,57 @@ public class MappingJacksonHttpMessageConverter extends AbstractHttpMessageConve
try {
return this.objectMapper.readValue(inputMessage.getBody(), javaType);
}
catch (JsonParseException ex) {
throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
}
catch (JsonMappingException ex) {
throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
}
catch (EOFException ex) {
catch (JsonProcessingException ex) {
throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
}
}
@Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = getEncoding(outputMessage.getHeaders().getContentType());
JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
JsonGenerator jsonGenerator =
this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
try {
if (this.prefixJson) {
jsonGenerator.writeRaw("{} && ");
}
this.objectMapper.writeValue(jsonGenerator, o);
this.objectMapper.writeValue(jsonGenerator, object);
}
catch (JsonGenerationException ex) {
catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
private JsonEncoding getEncoding(MediaType contentType) {
/**
* Return the Jackson {@link JavaType} for the specified class.
* <p>The default implementation returns {@link TypeFactory#type(java.lang.reflect.Type)},
* but this can be overridden in subclasses, to allow for custom generic collection handling.
* For instance:
* <pre class="code">
* protected JavaType getJavaType(Class&lt;?&gt; clazz) {
* if (List.class.isAssignableFrom(clazz)) {
* return TypeFactory.collectionType(ArrayList.class, MyBean.class);
* } else {
* return super.getJavaType(clazz);
* }
* }
* </pre>
* @param clazz the class to return the java type for
* @return the java type
*/
protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.type(clazz);
}
/**
* Determine the JSON encoding to use for the given content type.
* @param contentType the media type as requested by the caller
* @return the JSON encoding to use (never <code>null</code>)
*/
protected JsonEncoding getJsonEncoding(MediaType contentType) {
if (contentType != null && contentType.getCharSet() != null) {
Charset charset = contentType.getCharSet();
for (JsonEncoding encoding : JsonEncoding.values()) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册