Revised GsonFactoryBean's configuration properties; made prettyPrinting test pass on Windows
Issue: SPR-9488
This commit is contained in:
parent
d9f8ee886e
commit
22a38d4547
|
|
@ -18,81 +18,54 @@ package org.springframework.http.converter.json;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import com.google.gson.Gson;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import com.google.gson.GsonBuilder;
|
||||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.util.ClassUtils;
|
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.
|
* A {@link FactoryBean} for creating a Google Gson 2.x {@link Gson} instance.
|
||||||
*
|
*
|
||||||
* @author Roy Clarkson
|
* @author Roy Clarkson
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public class GsonFactoryBean implements FactoryBean<Gson>, BeanClassLoaderAware, InitializingBean {
|
public class GsonFactoryBean implements FactoryBean<Gson>, 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());
|
"org.apache.commons.codec.binary.Base64", GsonFactoryBean.class.getClassLoader());
|
||||||
|
|
||||||
private final Log logger = LogFactory.getLog(getClass());
|
|
||||||
|
|
||||||
|
|
||||||
private Gson gson;
|
|
||||||
|
|
||||||
private GsonBuilder gsonBuilder;
|
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
|
* Set the GsonBuilder instance to use.
|
||||||
* created using its default constructor.
|
* If not set, the GsonBuilder will be created using its default constructor.
|
||||||
*/
|
*/
|
||||||
public void setGsonBuilder(GsonBuilder gsonBuilder) {
|
public void setGsonBuilder(GsonBuilder gsonBuilder) {
|
||||||
this.gsonBuilder = gsonBuilder;
|
this.gsonBuilder = gsonBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the configured GsonBuilder instance to use, if any.
|
* Whether to use the {@link GsonBuilder#serializeNulls()} option when writing
|
||||||
* @return the GsonBuilder instance
|
|
||||||
*/
|
|
||||||
public GsonBuilder getGsonBuilder() {
|
|
||||||
return this.gsonBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to use the {@link GsonBuilder#setPrettyPrinting()} when writing
|
|
||||||
* JSON. This is a shortcut for setting up a {@code Gson} as follows:
|
* JSON. This is a shortcut for setting up a {@code Gson} as follows:
|
||||||
*
|
|
||||||
* <pre class="code">
|
|
||||||
* new GsonBuilder().setPrettyPrinting().create();
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
public void setPrettyPrint(boolean prettyPrint) {
|
|
||||||
this.prettyPrint = prettyPrint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to use the {@link GsonBuilder#serializeNulls()} option when
|
|
||||||
* writing JSON. This is a shortcut for setting up a {@code Gson} as
|
|
||||||
* follows:
|
|
||||||
*
|
|
||||||
* <pre class="code">
|
* <pre class="code">
|
||||||
* new GsonBuilder().serializeNulls().create();
|
* new GsonBuilder().serializeNulls().create();
|
||||||
* </pre>
|
* </pre>
|
||||||
|
|
@ -101,11 +74,21 @@ public class GsonFactoryBean implements FactoryBean<Gson>, BeanClassLoaderAware,
|
||||||
this.serializeNulls = serializeNulls;
|
this.serializeNulls = serializeNulls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use the {@link GsonBuilder#setPrettyPrinting()} when writing
|
||||||
|
* JSON. This is a shortcut for setting up a {@code Gson} as follows:
|
||||||
|
* <pre class="code">
|
||||||
|
* new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public void setPrettyPrinting(boolean prettyPrinting) {
|
||||||
|
this.prettyPrinting = prettyPrinting;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to use the {@link GsonBuilder#disableHtmlEscaping()} when writing
|
* Whether to use the {@link GsonBuilder#disableHtmlEscaping()} when writing
|
||||||
* JSON. Set to {@code true} to disable HTML escaping in JSON. This is a
|
* JSON. Set to {@code true} to disable HTML escaping in JSON. This is a
|
||||||
* shortcut for setting up a {@code Gson} as follows:
|
* shortcut for setting up a {@code Gson} as follows:
|
||||||
*
|
|
||||||
* <pre class="code">
|
* <pre class="code">
|
||||||
* new GsonBuilder().disableHtmlEscaping().create();
|
* new GsonBuilder().disableHtmlEscaping().create();
|
||||||
* </pre>
|
* </pre>
|
||||||
|
|
@ -115,92 +98,67 @@ public class GsonFactoryBean implements FactoryBean<Gson>, 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:
|
* This is a shortcut for setting up a {@code Gson} as follows:
|
||||||
*
|
|
||||||
* <pre class="code">
|
* <pre class="code">
|
||||||
* new GsonBuilder().setDateFormat(dateFormatPattern).create();
|
* new GsonBuilder().setDateFormat(dateFormatPattern).create();
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
|
||||||
* @see #setSimpleDateFormat(String)
|
|
||||||
*/
|
*/
|
||||||
public void setSimpleDateFormat(SimpleDateFormat dateFormat) {
|
public void setDateFormatPattern(String dateFormatPattern) {
|
||||||
this.dateFormat = dateFormat;
|
this.dateFormatPattern = dateFormatPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the date/time format with a {@link SimpleDateFormat}.
|
* Whether to Base64-encode {@code byte[]} properties when reading and
|
||||||
* This is a shortcut for setting up a {@code Gson} as follows:
|
|
||||||
*
|
|
||||||
* <pre class="code">
|
|
||||||
* new GsonBuilder().setDateFormat(dateFormatPattern).create();
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @see #setSimpleDateFormat(SimpleDateFormat)
|
|
||||||
*/
|
|
||||||
public void setSimpleDateFormat(String format) {
|
|
||||||
this.dateFormat = new SimpleDateFormat(format);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to Base64 encode {@code byte[]} properties when reading and
|
|
||||||
* writing JSON.
|
* writing JSON.
|
||||||
*
|
* <p>When set to {@code true} a custom {@link com.google.gson.TypeAdapter} is
|
||||||
* <p>When set to {@code true} a custom {@link com.google.gson.TypeAdapter}
|
* registered via {@link GsonBuilder#registerTypeHierarchyAdapter(Class, Object)}
|
||||||
* is registered via
|
* that serializes a {@code byte[]} property to and from a Base64-encoded String
|
||||||
* {@link GsonBuilder#registerTypeHierarchyAdapter(Class, Object)}
|
* instead of a JSON array.
|
||||||
* that serializes a {@code byte[]} property to and from a Base64 encoded
|
* <p><strong>NOTE:</strong> Use of this option requires the presence of the
|
||||||
* string instead of a JSON array.
|
* Apache Commons Codec library on the classpath.
|
||||||
*
|
* @see GsonBase64ByteArrayJsonTypeAdapter
|
||||||
* <p><strong>NOTE:</strong> 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
|
|
||||||
*/
|
*/
|
||||||
public void setBase64EncodeByteArrays(boolean base64EncodeByteArrays) {
|
public void setBase64EncodeByteArrays(boolean base64EncodeByteArrays) {
|
||||||
this.base64EncodeByteArrays = base64EncodeByteArrays;
|
this.base64EncodeByteArrays = base64EncodeByteArrays;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
|
||||||
this.beanClassLoader = beanClassLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() {
|
||||||
if (this.gsonBuilder == null) {
|
if (this.gsonBuilder == null) {
|
||||||
this.gsonBuilder = new GsonBuilder();
|
this.gsonBuilder = new GsonBuilder();
|
||||||
}
|
}
|
||||||
if (this.prettyPrint != null && this.prettyPrint) {
|
if (this.serializeNulls) {
|
||||||
this.gsonBuilder = this.gsonBuilder.setPrettyPrinting();
|
this.gsonBuilder.serializeNulls();
|
||||||
}
|
}
|
||||||
if (this.serializeNulls != null && this.serializeNulls) {
|
if (this.prettyPrinting) {
|
||||||
this.gsonBuilder = this.gsonBuilder.serializeNulls();
|
this.gsonBuilder.setPrettyPrinting();
|
||||||
}
|
}
|
||||||
if (this.disableHtmlEscaping != null && this.disableHtmlEscaping) {
|
if (this.disableHtmlEscaping) {
|
||||||
this.gsonBuilder = this.gsonBuilder.disableHtmlEscaping();
|
this.gsonBuilder.disableHtmlEscaping();
|
||||||
}
|
}
|
||||||
if (this.dateFormat != null) {
|
if (this.dateFormatPattern != null) {
|
||||||
this.gsonBuilder.setDateFormat(this.dateFormat.toPattern());
|
this.gsonBuilder.setDateFormat(this.dateFormatPattern);
|
||||||
}
|
}
|
||||||
if (base64Present) {
|
if (this.base64EncodeByteArrays) {
|
||||||
if (this.base64EncodeByteArrays != null && this.base64EncodeByteArrays) {
|
if (commonsCodecPresent) {
|
||||||
this.gsonBuilder.registerTypeHierarchyAdapter(byte[].class, new GsonBase64ByteArrayJsonTypeAdapter());
|
this.gsonBuilder.registerTypeHierarchyAdapter(byte[].class, new GsonBase64ByteArrayJsonTypeAdapter());
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
else if (logger.isDebugEnabled()) {
|
throw new IllegalStateException(
|
||||||
logger.debug("org.apache.commons.codec.binary.Base64 is not " +
|
"Apache Commons Codec is not available on the classpath - cannot enable Gson Base64 encoding");
|
||||||
"available on the class path. Gson Base64 encoding is disabled.");
|
}
|
||||||
}
|
}
|
||||||
this.gson = this.gsonBuilder.create();
|
this.gson = this.gsonBuilder.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the created Gson instance.
|
* Return the created Gson instance.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Gson getObject() throws Exception {
|
public Gson getObject() {
|
||||||
return this.gson;
|
return this.gson;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,11 @@
|
||||||
|
|
||||||
package org.springframework.http.converter.json;
|
package org.springframework.http.converter.json;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
|
@ -36,37 +33,9 @@ public class GsonFactoryBeanTests {
|
||||||
|
|
||||||
private static final String DATE_FORMAT = "yyyy-MM-dd";
|
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);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void prettyPrintFalse() throws Exception {
|
|
||||||
this.factory.setPrettyPrint(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void serializeNulls() throws Exception {
|
public void serializeNulls() throws Exception {
|
||||||
this.factory.setSerializeNulls(true);
|
this.factory.setSerializeNulls(true);
|
||||||
|
|
@ -87,6 +56,28 @@ public class GsonFactoryBeanTests {
|
||||||
assertEquals("{}", result);
|
assertEquals("{}", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
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);
|
||||||
|
assertTrue(result.contains(" \"name\": \"Jason\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
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("{\"name\":\"Jason\"}", result);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void disableHtmlEscaping() throws Exception {
|
public void disableHtmlEscaping() throws Exception {
|
||||||
this.factory.setDisableHtmlEscaping(true);
|
this.factory.setDisableHtmlEscaping(true);
|
||||||
|
|
@ -110,26 +101,8 @@ public class GsonFactoryBeanTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customizeDateFormat() throws Exception {
|
public void customizeDateFormatPattern() throws Exception {
|
||||||
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
|
this.factory.setDateFormatPattern(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);
|
|
||||||
this.factory.afterPropertiesSet();
|
this.factory.afterPropertiesSet();
|
||||||
Gson gson = this.factory.getObject();
|
Gson gson = this.factory.getObject();
|
||||||
DateBean bean = new DateBean();
|
DateBean bean = new DateBean();
|
||||||
|
|
@ -166,7 +139,7 @@ public class GsonFactoryBeanTests {
|
||||||
this.factory.afterPropertiesSet();
|
this.factory.afterPropertiesSet();
|
||||||
Gson gson = this.factory.getObject();
|
Gson gson = this.factory.getObject();
|
||||||
ByteArrayBean bean = new ByteArrayBean();
|
ByteArrayBean bean = new ByteArrayBean();
|
||||||
bean.setBytes(new byte[] { 0x1, 0x2 });
|
bean.setBytes(new byte[] {0x1, 0x2});
|
||||||
String result = gson.toJson(bean);
|
String result = gson.toJson(bean);
|
||||||
assertEquals("{\"bytes\":\"AQI\\u003d\"}", result);
|
assertEquals("{\"bytes\":\"AQI\\u003d\"}", result);
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +151,7 @@ public class GsonFactoryBeanTests {
|
||||||
this.factory.afterPropertiesSet();
|
this.factory.afterPropertiesSet();
|
||||||
Gson gson = this.factory.getObject();
|
Gson gson = this.factory.getObject();
|
||||||
ByteArrayBean bean = new ByteArrayBean();
|
ByteArrayBean bean = new ByteArrayBean();
|
||||||
bean.setBytes(new byte[] { 0x1, 0x2 });
|
bean.setBytes(new byte[] {0x1, 0x2});
|
||||||
String result = gson.toJson(bean);
|
String result = gson.toJson(bean);
|
||||||
assertEquals("{\"bytes\":\"AQI=\"}", result);
|
assertEquals("{\"bytes\":\"AQI=\"}", result);
|
||||||
}
|
}
|
||||||
|
|
@ -189,7 +162,7 @@ public class GsonFactoryBeanTests {
|
||||||
this.factory.afterPropertiesSet();
|
this.factory.afterPropertiesSet();
|
||||||
Gson gson = this.factory.getObject();
|
Gson gson = this.factory.getObject();
|
||||||
ByteArrayBean bean = new ByteArrayBean();
|
ByteArrayBean bean = new ByteArrayBean();
|
||||||
bean.setBytes(new byte[] { 0x1, 0x2 });
|
bean.setBytes(new byte[] {0x1, 0x2});
|
||||||
String result = gson.toJson(bean);
|
String result = gson.toJson(bean);
|
||||||
assertEquals("{\"bytes\":[1,2]}", result);
|
assertEquals("{\"bytes\":[1,2]}", result);
|
||||||
}
|
}
|
||||||
|
|
@ -208,6 +181,7 @@ public class GsonFactoryBeanTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class DateBean {
|
private static class DateBean {
|
||||||
|
|
||||||
private Date date;
|
private Date date;
|
||||||
|
|
@ -221,7 +195,8 @@ public class GsonFactoryBeanTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ByteArrayBean {
|
|
||||||
|
private static class ByteArrayBean {
|
||||||
|
|
||||||
private byte[] bytes;
|
private byte[] bytes;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue