MarshallingMessageConverter 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 MarshallingMessageConverter 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.");
}
}
diff --git a/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java b/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java
index d6521ff5dd3..7eb474af4ce 100644
--- a/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java
+++ b/org.springframework.oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java
@@ -1,5 +1,5 @@
/*
- * 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(MapNote 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. + *
Note 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}. - *
Default implementation is empty. + *
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 diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/json/MappingJacksonJsonView.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/json/MappingJacksonJsonView.java index 9e1c9364c16..cdc59c24eff 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/json/MappingJacksonJsonView.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/json/MappingJacksonJsonView.java @@ -1,5 +1,5 @@ /* - * 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. @@ -20,7 +20,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -28,6 +27,7 @@ import org.codehaus.jackson.JsonEncoding; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializerFactory; + import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.validation.BindingResult; @@ -35,19 +35,18 @@ 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 Jackson's {@link ObjectMapper}. + * Spring MVC {@link View} that renders JSON content by serializing the model for the current request + * using Jackson's {@link ObjectMapper}. * - *
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(boolean)}. Or you can select specific model attributes to be encoded - * as JSON via ... TODO + *
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 * @author Rossen Stoyanchev - * @see org.springframework.http.converter.json.MappingJacksonHttpMessageConverter * @since 3.0 + * @see org.springframework.http.converter.json.MappingJacksonHttpMessageConverter */ public class MappingJacksonJsonView extends AbstractView { @@ -56,6 +55,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; @@ -68,6 +68,7 @@ public class MappingJacksonJsonView extends AbstractView { private boolean disableCaching = true; + /** * Construct a new {@code JacksonJsonView}, setting the content type to {@code application/json}. */ @@ -76,14 +77,15 @@ public class MappingJacksonJsonView extends AbstractView { setExposePathVariables(false); } + /** - * Sets the {@code ObjectMapper} for this view. If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} - * is used. - * - *
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. + *
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"); @@ -91,7 +93,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"); @@ -99,18 +102,19 @@ public class MappingJacksonJsonView extends AbstractView { } /** - * Indicates whether the JSON output by this view should be prefixed with "{} && ". Default is false. - * - *
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 "{} && ". + * Default is false. + *
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;
}
-
+
/**
- * Sets the attribute in the model that should be rendered by this view.
+ * Set the attribute in the model that should be rendered by this view.
* When set, all other model attributes will be ignored.
*/
public void setModelKey(String modelKey) {
@@ -118,7 +122,7 @@ public class MappingJacksonJsonView extends AbstractView {
}
/**
- * Sets 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 void setModelKeys(Set The effect of setting this flag is similar to using {@code MappingJacksonHttpMessageConverter}
+ * with an {@code @ResponseBody} request-handling method.
+ * Default is {@code false}.
+ */
+ public void setExtractValueFromSingleKeyModel(boolean extractValueFromSingleKeyModel) {
+ this.extractValueFromSingleKeyModel = extractValueFromSingleKeyModel;
}
/**
* Disables caching of the generated JSON.
- *
* Default is {@code true}, which will prevent the client from caching the generated JSON.
*/
public void setDisableCaching(boolean disableCaching) {
this.disableCaching = disableCaching;
}
- /**
- * Set whether to serialize models containing a single attribute as a map or whether to
- * extract the single value from the model and serialize it directly.
- * The effect of setting this flag is similar to using
- * {@code MappingJacksonHttpMessageConverter} with an {@code @ResponseBody}.
- * request-handling method.
- *
- * Default is {@code false}.
- */
- public void setExtractValueFromSingleKeyModel(boolean extractValueFromSingleKeyModel) {
- this.extractValueFromSingleKeyModel = extractValueFromSingleKeyModel;
- }
@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);
@@ -183,38 +187,35 @@ public class MappingJacksonJsonView extends AbstractView {
}
@Override
- protected void renderMergedOutputModel(Map 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.
+ * 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 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:
- * 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:
+ *
- * protected JavaType getJavaType(Class<?> clazz) {
- * if (List.class.isAssignableFrom(clazz)) {
- * return TypeFactory.collectionType(ArrayList.class, MyBean.class);
- * } else {
- * return super.getJavaType(clazz);
- * }
- * }
- *
- * @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.
+ *
+ * protected JavaType getJavaType(Class<?> clazz) {
+ * if (List.class.isAssignableFrom(clazz)) {
+ * return TypeFactory.collectionType(ArrayList.class, MyBean.class);
+ * } else {
+ * return super.getJavaType(clazz);
+ * }
+ * }
+ *
+ * @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 null)
+ */
+ protected JsonEncoding getJsonEncoding(MediaType contentType) {
if (contentType != null && contentType.getCharSet() != null) {
Charset charset = contentType.getCharSet();
for (JsonEncoding encoding : JsonEncoding.values()) {