diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/GsonFactoryBean.java b/spring-web/src/main/java/org/springframework/http/converter/json/GsonFactoryBean.java index 8355e037046..25ab9af4349 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/GsonFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/GsonFactoryBean.java @@ -18,94 +18,77 @@ package org.springframework.http.converter.json; import java.text.SimpleDateFormat; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.BeanClassLoaderAware; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.ClassUtils; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - /** * A {@link FactoryBean} for creating a Google Gson 2.x {@link Gson} instance. * * @author Roy Clarkson + * @author Juergen Hoeller * @since 4.1 */ -public class GsonFactoryBean implements FactoryBean, BeanClassLoaderAware, InitializingBean { +public class GsonFactoryBean implements FactoryBean, InitializingBean { - private static final boolean base64Present = ClassUtils.isPresent( + /** Apache Commons Codec present on the classpath, for Base64 encoding? */ + private static final boolean commonsCodecPresent = ClassUtils.isPresent( "org.apache.commons.codec.binary.Base64", GsonFactoryBean.class.getClassLoader()); - private final Log logger = LogFactory.getLog(getClass()); - - - private Gson gson; private GsonBuilder gsonBuilder; - private Boolean prettyPrint; + private boolean serializeNulls; - private Boolean serializeNulls; + private boolean prettyPrinting; - private Boolean disableHtmlEscaping; + private boolean disableHtmlEscaping; - private SimpleDateFormat dateFormat; + private String dateFormatPattern; - private Boolean base64EncodeByteArrays; + private boolean base64EncodeByteArrays; - private ClassLoader beanClassLoader; + private Gson gson; /** - * Set the GsonBuilder instance to use. If not set, the GsonBuilder will be - * created using its default constructor. + * Set the GsonBuilder instance to use. + * If not set, the GsonBuilder will be created using its default constructor. */ public void setGsonBuilder(GsonBuilder gsonBuilder) { this.gsonBuilder = gsonBuilder; } /** - * Return the configured GsonBuilder instance to use, if any. - * @return the GsonBuilder instance - */ - public GsonBuilder getGsonBuilder() { - return this.gsonBuilder; - } - - /** - * Whether to use the {@link GsonBuilder#setPrettyPrinting()} when writing + * Whether to use the {@link GsonBuilder#serializeNulls()} option when writing * JSON. This is a shortcut for setting up a {@code Gson} as follows: - * *
-	 * new GsonBuilder().setPrettyPrinting().create();
+	 * new GsonBuilder().serializeNulls().create();
 	 * 
*/ - public void setPrettyPrint(boolean prettyPrint) { - this.prettyPrint = prettyPrint; + public void setSerializeNulls(boolean serializeNulls) { + this.serializeNulls = serializeNulls; } /** - * Whether to use the {@link GsonBuilder#serializeNulls()} option when - * writing JSON. This is a shortcut for setting up a {@code Gson} as - * follows: - * + * Whether to use the {@link GsonBuilder#setPrettyPrinting()} when writing + * JSON. This is a shortcut for setting up a {@code Gson} as follows: *
-	 * new GsonBuilder().serializeNulls().create();
+	 * new GsonBuilder().setPrettyPrinting().create();
 	 * 
*/ - public void setSerializeNulls(boolean serializeNulls) { - this.serializeNulls = serializeNulls; + public void setPrettyPrinting(boolean prettyPrinting) { + this.prettyPrinting = prettyPrinting; } /** * Whether to use the {@link GsonBuilder#disableHtmlEscaping()} when writing * JSON. Set to {@code true} to disable HTML escaping in JSON. This is a * shortcut for setting up a {@code Gson} as follows: - * *
 	 * new GsonBuilder().disableHtmlEscaping().create();
 	 * 
@@ -115,92 +98,67 @@ public class GsonFactoryBean implements FactoryBean, BeanClassLoaderAware, } /** - * Define the format for date/time with the given {@link SimpleDateFormat}. + * Define the date/time format with a {@link SimpleDateFormat}-style pattern. * This is a shortcut for setting up a {@code Gson} as follows: - * *
 	 * new GsonBuilder().setDateFormat(dateFormatPattern).create();
 	 * 
- * - * @see #setSimpleDateFormat(String) */ - public void setSimpleDateFormat(SimpleDateFormat dateFormat) { - this.dateFormat = dateFormat; + public void setDateFormatPattern(String dateFormatPattern) { + this.dateFormatPattern = dateFormatPattern; } /** - * Define the date/time format with a {@link SimpleDateFormat}. - * This is a shortcut for setting up a {@code Gson} as follows: - * - *
-	 * new GsonBuilder().setDateFormat(dateFormatPattern).create();
-	 * 
- * - * @see #setSimpleDateFormat(SimpleDateFormat) - */ - public void setSimpleDateFormat(String format) { - this.dateFormat = new SimpleDateFormat(format); - } - - /** - * Whether to Base64 encode {@code byte[]} properties when reading and + * Whether to Base64-encode {@code byte[]} properties when reading and * writing JSON. - * - *

When set to {@code true} a custom {@link com.google.gson.TypeAdapter} - * is registered via - * {@link GsonBuilder#registerTypeHierarchyAdapter(Class, Object)} - * that serializes a {@code byte[]} property to and from a Base64 encoded - * string instead of a JSON array. - * - *

NOTE: Use of this option requires the presence of - * Apache commons-codec on the classpath. Otherwise it is ignored. - * - * @see org.springframework.http.converter.json.GsonBase64ByteArrayJsonTypeAdapter + *

When set to {@code true} a custom {@link com.google.gson.TypeAdapter} is + * registered via {@link GsonBuilder#registerTypeHierarchyAdapter(Class, Object)} + * that serializes a {@code byte[]} property to and from a Base64-encoded String + * instead of a JSON array. + *

NOTE: Use of this option requires the presence of the + * Apache Commons Codec library on the classpath. + * @see GsonBase64ByteArrayJsonTypeAdapter */ public void setBase64EncodeByteArrays(boolean base64EncodeByteArrays) { this.base64EncodeByteArrays = base64EncodeByteArrays; } - @Override - public void setBeanClassLoader(ClassLoader beanClassLoader) { - this.beanClassLoader = beanClassLoader; - } - @Override - public void afterPropertiesSet() throws Exception { + public void afterPropertiesSet() { if (this.gsonBuilder == null) { this.gsonBuilder = new GsonBuilder(); } - if (this.prettyPrint != null && this.prettyPrint) { - this.gsonBuilder = this.gsonBuilder.setPrettyPrinting(); + if (this.serializeNulls) { + this.gsonBuilder.serializeNulls(); } - if (this.serializeNulls != null && this.serializeNulls) { - this.gsonBuilder = this.gsonBuilder.serializeNulls(); + if (this.prettyPrinting) { + this.gsonBuilder.setPrettyPrinting(); } - if (this.disableHtmlEscaping != null && this.disableHtmlEscaping) { - this.gsonBuilder = this.gsonBuilder.disableHtmlEscaping(); + if (this.disableHtmlEscaping) { + this.gsonBuilder.disableHtmlEscaping(); } - if (this.dateFormat != null) { - this.gsonBuilder.setDateFormat(this.dateFormat.toPattern()); + if (this.dateFormatPattern != null) { + this.gsonBuilder.setDateFormat(this.dateFormatPattern); } - if (base64Present) { - if (this.base64EncodeByteArrays != null && this.base64EncodeByteArrays) { + if (this.base64EncodeByteArrays) { + if (commonsCodecPresent) { this.gsonBuilder.registerTypeHierarchyAdapter(byte[].class, new GsonBase64ByteArrayJsonTypeAdapter()); } - } - else if (logger.isDebugEnabled()) { - logger.debug("org.apache.commons.codec.binary.Base64 is not " + - "available on the class path. Gson Base64 encoding is disabled."); + else { + throw new IllegalStateException( + "Apache Commons Codec is not available on the classpath - cannot enable Gson Base64 encoding"); + } } this.gson = this.gsonBuilder.create(); } + /** * Return the created Gson instance. */ @Override - public Gson getObject() throws Exception { + public Gson getObject() { return this.gson; } diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/GsonFactoryBeanTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/GsonFactoryBeanTests.java index 490baf05cb8..382a291cee9 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/json/GsonFactoryBeanTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/json/GsonFactoryBeanTests.java @@ -16,14 +16,11 @@ package org.springframework.http.converter.json; -import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; -import org.junit.Before; -import org.junit.Test; - import com.google.gson.Gson; +import org.junit.Test; import static org.junit.Assert.*; @@ -36,55 +33,49 @@ public class GsonFactoryBeanTests { private static final String DATE_FORMAT = "yyyy-MM-dd"; - private GsonFactoryBean factory; + private GsonFactoryBean factory = new GsonFactoryBean(); - @Before - public void setUp() { - factory = new GsonFactoryBean(); - } - @Test - public void prettyPrint() throws Exception { - this.factory.setPrettyPrint(true); + public void serializeNulls() throws Exception { + this.factory.setSerializeNulls(true); this.factory.afterPropertiesSet(); Gson gson = this.factory.getObject(); StringBean bean = new StringBean(); - bean.setName("Jason"); String result = gson.toJson(bean); - String lineSeparator = System.getProperty("line.separator"); - assertEquals("{" + lineSeparator + " \"name\": \"Jason\"" + lineSeparator + "}", result); + assertEquals("{\"name\":null}", result); } @Test - public void prettyPrintFalse() throws Exception { - this.factory.setPrettyPrint(false); + public void serializeNullsFalse() throws Exception { + this.factory.setSerializeNulls(false); this.factory.afterPropertiesSet(); Gson gson = this.factory.getObject(); StringBean bean = new StringBean(); - bean.setName("Jason"); String result = gson.toJson(bean); - assertEquals("{\"name\":\"Jason\"}", result); + assertEquals("{}", result); } @Test - public void serializeNulls() throws Exception { - this.factory.setSerializeNulls(true); + public void prettyPrinting() throws Exception { + this.factory.setPrettyPrinting(true); this.factory.afterPropertiesSet(); Gson gson = this.factory.getObject(); StringBean bean = new StringBean(); + bean.setName("Jason"); String result = gson.toJson(bean); - assertEquals("{\"name\":null}", result); + assertTrue(result.contains(" \"name\": \"Jason\"")); } @Test - public void serializeNullsFalse() throws Exception { - this.factory.setSerializeNulls(false); + public void prettyPrintingFalse() throws Exception { + this.factory.setPrettyPrinting(false); this.factory.afterPropertiesSet(); Gson gson = this.factory.getObject(); StringBean bean = new StringBean(); + bean.setName("Jason"); String result = gson.toJson(bean); - assertEquals("{}", result); + assertEquals("{\"name\":\"Jason\"}", result); } @Test @@ -110,26 +101,8 @@ public class GsonFactoryBeanTests { } @Test - public void customizeDateFormat() throws Exception { - SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); - this.factory.setSimpleDateFormat(dateFormat); - this.factory.afterPropertiesSet(); - Gson gson = this.factory.getObject(); - DateBean bean = new DateBean(); - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(Calendar.YEAR, 2014); - cal.set(Calendar.MONTH, Calendar.JANUARY); - cal.set(Calendar.DATE, 1); - Date date = cal.getTime(); - bean.setDate(date); - String result = gson.toJson(bean); - assertEquals("{\"date\":\"2014-01-01\"}", result); - } - - @Test - public void customizeDateFormatString() throws Exception { - this.factory.setSimpleDateFormat(DATE_FORMAT); + public void customizeDateFormatPattern() throws Exception { + this.factory.setDateFormatPattern(DATE_FORMAT); this.factory.afterPropertiesSet(); Gson gson = this.factory.getObject(); DateBean bean = new DateBean(); @@ -166,7 +139,7 @@ public class GsonFactoryBeanTests { this.factory.afterPropertiesSet(); Gson gson = this.factory.getObject(); ByteArrayBean bean = new ByteArrayBean(); - bean.setBytes(new byte[] { 0x1, 0x2 }); + bean.setBytes(new byte[] {0x1, 0x2}); String result = gson.toJson(bean); assertEquals("{\"bytes\":\"AQI\\u003d\"}", result); } @@ -178,7 +151,7 @@ public class GsonFactoryBeanTests { this.factory.afterPropertiesSet(); Gson gson = this.factory.getObject(); ByteArrayBean bean = new ByteArrayBean(); - bean.setBytes(new byte[] { 0x1, 0x2 }); + bean.setBytes(new byte[] {0x1, 0x2}); String result = gson.toJson(bean); assertEquals("{\"bytes\":\"AQI=\"}", result); } @@ -189,7 +162,7 @@ public class GsonFactoryBeanTests { this.factory.afterPropertiesSet(); Gson gson = this.factory.getObject(); ByteArrayBean bean = new ByteArrayBean(); - bean.setBytes(new byte[] { 0x1, 0x2 }); + bean.setBytes(new byte[] {0x1, 0x2}); String result = gson.toJson(bean); assertEquals("{\"bytes\":[1,2]}", result); } @@ -208,6 +181,7 @@ public class GsonFactoryBeanTests { } } + private static class DateBean { private Date date; @@ -221,7 +195,8 @@ public class GsonFactoryBeanTests { } } - public static class ByteArrayBean { + + private static class ByteArrayBean { private byte[] bytes;