11/*
2- * Copyright 2002-2012 the original author or authors.
2+ * Copyright 2002-2013 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1717package org .springframework .web .servlet .view .json ;
1818
1919import java .io .ByteArrayOutputStream ;
20+ import java .io .IOException ;
2021import java .io .OutputStream ;
2122import java .util .Collections ;
2223import java .util .HashMap ;
2728
2829import com .fasterxml .jackson .core .JsonEncoding ;
2930import com .fasterxml .jackson .core .JsonGenerator ;
30- import com .fasterxml .jackson .core .util .DefaultPrettyPrinter ;
3131import com .fasterxml .jackson .databind .ObjectMapper ;
3232import com .fasterxml .jackson .databind .SerializationFeature ;
3333
4848 * @author Jeremy Grelle
4949 * @author Arjen Poutsma
5050 * @author Rossen Stoyanchev
51+ * @author Juergen Hoeller
5152 * @since 3.1.2
5253 * @see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
5354 */
5455public class MappingJackson2JsonView extends AbstractView {
5556
5657 /**
57- * Default content type. Overridable as bean property.
58+ * Default content type: "application/json".
59+ * Overridable through {@link #setContentType}.
5860 */
5961 public static final String DEFAULT_CONTENT_TYPE = "application/json" ;
6062
@@ -75,8 +77,9 @@ public class MappingJackson2JsonView extends AbstractView {
7577
7678 private boolean updateContentLength = false ;
7779
80+
7881 /**
79- * Construct a new {@code JacksonJsonView }, setting the content type to {@code application/json}.
82+ * Construct a new {@code MappingJackson2JsonView }, setting the content type to {@code application/json}.
8083 */
8184 public MappingJackson2JsonView () {
8285 setContentType (DEFAULT_CONTENT_TYPE );
@@ -85,38 +88,44 @@ public MappingJackson2JsonView() {
8588
8689
8790 /**
88- * Sets the {@code ObjectMapper} for this view.
89- * If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} is used.
90- * <p>Setting a custom-configured {@code ObjectMapper} is one way to take further control
91- * of the JSON serialization process. For example, an extended {@code SerializerFactory}
92- * can be configured that provides custom serializers for specific types. The other option
93- * for refining the serialization process is to use Jackson's provided annotations on the
94- * types to be serialized, in which case a custom-configured ObjectMapper is unnecessary.
91+ * Set the {@code ObjectMapper} for this view.
92+ * If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} will be used.
93+ * <p>Setting a custom-configured {@code ObjectMapper} is one way to take further control of
94+ * the JSON serialization process. The other option is to use Jackson's provided annotations
95+ * on the types to be serialized, in which case a custom-configured ObjectMapper is unnecessary.
9596 */
9697 public void setObjectMapper (ObjectMapper objectMapper ) {
9798 Assert .notNull (objectMapper , "'objectMapper' must not be null" );
9899 this .objectMapper = objectMapper ;
99100 configurePrettyPrint ();
100101 }
101102
102- private void configurePrettyPrint () {
103- if (this .prettyPrint != null ) {
104- this .objectMapper .configure (SerializationFeature .INDENT_OUTPUT , this .prettyPrint );
105- }
103+ /**
104+ * Return the {@code ObjectMapper} for this view.
105+ */
106+ public final ObjectMapper getObjectMapper () {
107+ return this .objectMapper ;
106108 }
107109
108110 /**
109- * Set the {@code JsonEncoding} for this converter .
111+ * Set the {@code JsonEncoding} for this view .
110112 * By default, {@linkplain JsonEncoding#UTF8 UTF-8} is used.
111113 */
112114 public void setEncoding (JsonEncoding encoding ) {
113115 Assert .notNull (encoding , "'encoding' must not be null" );
114116 this .encoding = encoding ;
115117 }
116118
119+ /**
120+ * Return the {@code JsonEncoding} for this view.
121+ */
122+ public final JsonEncoding getEncoding () {
123+ return this .encoding ;
124+ }
125+
117126 /**
118127 * Indicates whether the JSON output by this view should be prefixed with <tt>"{} && "</tt>.
119- * Default is false.
128+ * Default is {@code false} .
120129 * <p>Prefixing the JSON string in this manner is used to help prevent JSON Hijacking.
121130 * The prefix renders the string syntactically invalid as a script so that it cannot be hijacked.
122131 * This prefix does not affect the evaluation of JSON, but if JSON validation is performed
@@ -127,12 +136,11 @@ public void setPrefixJson(boolean prefixJson) {
127136 }
128137
129138 /**
130- * Whether to use the {@link DefaultPrettyPrinter} when writing JSON.
139+ * Whether to use the default pretty printer when writing JSON.
131140 * This is a shortcut for setting up an {@code ObjectMapper} as follows:
132141 * <pre>
133142 * ObjectMapper mapper = new ObjectMapper();
134143 * mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
135- * converter.setObjectMapper(mapper);
136144 * </pre>
137145 * <p>The default value is {@code false}.
138146 */
@@ -141,6 +149,12 @@ public void setPrettyPrint(boolean prettyPrint) {
141149 configurePrettyPrint ();
142150 }
143151
152+ private void configurePrettyPrint () {
153+ if (this .prettyPrint != null ) {
154+ this .objectMapper .configure (SerializationFeature .INDENT_OUTPUT , this .prettyPrint );
155+ }
156+ }
157+
144158 /**
145159 * Set the attribute in the model that should be rendered by this view.
146160 * When set, all other model attributes will be ignored.
@@ -160,7 +174,7 @@ public void setModelKeys(Set<String> modelKeys) {
160174 /**
161175 * Return the attributes in the model that should be rendered by this view.
162176 */
163- public Set <String > getModelKeys () {
177+ public final Set <String > getModelKeys () {
164178 return this .modelKeys ;
165179 }
166180
@@ -179,7 +193,7 @@ public void setRenderedAttributes(Set<String> renderedAttributes) {
179193 * @deprecated use {@link #getModelKeys()} instead
180194 */
181195 @ Deprecated
182- public Set <String > getRenderedAttributes () {
196+ public final Set <String > getRenderedAttributes () {
183197 return this .modelKeys ;
184198 }
185199
@@ -212,6 +226,7 @@ public void setUpdateContentLength(boolean updateContentLength) {
212226 this .updateContentLength = updateContentLength ;
213227 }
214228
229+
215230 @ Override
216231 protected void prepareResponse (HttpServletRequest request , HttpServletResponse response ) {
217232 setResponseContentType (request , response );
@@ -227,34 +242,21 @@ protected void prepareResponse(HttpServletRequest request, HttpServletResponse r
227242 protected void renderMergedOutputModel (Map <String , Object > model , HttpServletRequest request ,
228243 HttpServletResponse response ) throws Exception {
229244
230- OutputStream stream = this .updateContentLength ? createTemporaryOutputStream () : response .getOutputStream ();
231-
245+ OutputStream stream = (this .updateContentLength ? createTemporaryOutputStream () : response .getOutputStream ());
232246 Object value = filterModel (model );
233- JsonGenerator generator = this .objectMapper .getJsonFactory ().createJsonGenerator (stream , this .encoding );
234-
235- // A workaround for JsonGenerators not applying serialization features
236- // https://github.com/FasterXML/jackson-databind/issues/12
237- if (this .objectMapper .isEnabled (SerializationFeature .INDENT_OUTPUT )) {
238- generator .useDefaultPrettyPrinter ();
239- }
240-
241- if (this .prefixJson ) {
242- generator .writeRaw ("{} && " );
243- }
244- this .objectMapper .writeValue (generator , value );
245-
247+ writeContent (stream , value , this .prefixJson );
246248 if (this .updateContentLength ) {
247249 writeToResponse (response , (ByteArrayOutputStream ) stream );
248250 }
249251 }
250252
251253 /**
252- * Filters out undesired attributes from the given model.
254+ * Filter out undesired attributes from the given model.
253255 * The return value can be either another {@link Map} or a single value object.
254256 * <p>The default implementation removes {@link BindingResult} instances and entries
255257 * not included in the {@link #setRenderedAttributes renderedAttributes} property.
256258 * @param model the model, as passed on to {@link #renderMergedOutputModel}
257- * @return the object to be rendered
259+ * @return the value to be rendered
258260 */
259261 protected Object filterModel (Map <String , Object > model ) {
260262 Map <String , Object > result = new HashMap <String , Object >(model .size ());
@@ -267,4 +269,27 @@ protected Object filterModel(Map<String, Object> model) {
267269 return (this .extractValueFromSingleKeyModel && result .size () == 1 ? result .values ().iterator ().next () : result );
268270 }
269271
272+ /**
273+ * Write the actual JSON content to the stream.
274+ * @param stream the output stream to use
275+ * @param value the value to be rendered, as returned from {@link #filterModel}
276+ * @param prefixJson whether the JSON output by this view should be prefixed
277+ * with <tt>"{} && "</tt> (as indicated through {@link #setPrefixJson})
278+ * @throws IOException if writing failed
279+ */
280+ protected void writeContent (OutputStream stream , Object value , boolean prefixJson ) throws IOException {
281+ JsonGenerator generator = this .objectMapper .getJsonFactory ().createJsonGenerator (stream , this .encoding );
282+
283+ // A workaround for JsonGenerators not applying serialization features
284+ // https://github.com/FasterXML/jackson-databind/issues/12
285+ if (this .objectMapper .isEnabled (SerializationFeature .INDENT_OUTPUT )) {
286+ generator .useDefaultPrettyPrinter ();
287+ }
288+
289+ if (prefixJson ) {
290+ generator .writeRaw ("{} && " );
291+ }
292+ this .objectMapper .writeValue (generator , value );
293+ }
294+
270295}
0 commit comments