问题描述: ssm框架中
实体类属性值为null时Gson序列化返回的json对应值为null
最近几天在给app开发接口,使用了Gson进行json的序列/反序列化,虽然Gson在使用上和性能上又优点,但是美中不足的是实体类属性为null是返回的json只有两种结果(注意我说的属性值都是String类型,Gson版本为2.8)
1.返回的json中实体类属性值为null的对应的为null
Gson gson = new GsonBuilder()//建造者模式设置不同的配置
.serializeNulls()//序列化为null对象
.setDateFormat("yyyy-MM-dd HH:mm:ss") //设置日期的格式
.disableHtmlEscaping()//防止对网址乱码 忽略对特殊字符的转换
.create();
如所示
{
"uId": 1,
"token": null,
"code": 200,
"userName": "admin"
}
2.返回的json中忽略了实体类属性值为null的属性
Gson gson = new GsonBuilder()//建造者模式设置不同的配置
.setDateFormat("yyyy-MM-dd HH:mm:ss") //设置日期的格式
.disableHtmlEscaping()//防止对网址乱码 忽略对特殊字符的转换
.create();
如所示
{
"uId": 1,
"code": 200,
"userName": "admin"
}
两者不同请自行对比,然而这两种模式对于app来说都是无法使用的,最好的办法就是实体类属性值为空的时候返回" "(空字符串)。
- 具体思路
1.自定义MyHttpMessagerCovert转换器继承GsonHttpMessageConverter
2.自定义NullStringToEmptyAdapterFactory和StringNullAdapter,重写write方法,使用""替代返回的nullValue()
3.重新配置GsonBuilder生成我们需要的gson对象,使用自定义的适配器
spring-mvc.xml中配置
<!--使用自定义的转换器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"
p:ignoreDefaultModelOnRedirect="true" >
<property name="messageConverters">
<list>
<bean class="com.xqxf.utils.MyHttpMessagerCovert">
<!--避免IE执行Ajax时,返回JSON出现下载文件 -->
<!-- 自定义 -->
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
- MyHttpMessagerCovert 类实现
package com.xqxf.utils.Json;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.google.gson.reflect.TypeToken;
import com.xqxf.dto.UserDto;
import com.xqxf.dto.WorkOrderDto;
import com.xqxf.utils.Json.NullStringToEmptyAdapterFactory;
import com.xqxf.utils.Json.UserDeserializer;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.ref.PhantomReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
/**
* Package com.xqxf.utils
*
* author liyaocai
*
* date 2017/10/1 14:39
*
* Description:
*
* version V1.0
*/
public class MyHttpMessagerCovert extends GsonHttpMessageConverter {
private Gson gson = new GsonBuilder()//建造者模式设置不同的配置
.serializeNulls()//序列化为null对象
.setDateFormat("yyyy-MM-dd HH:mm:ss") //设置日期的格式
.disableHtmlEscaping()//防止对网址乱码 忽略对特殊字符的转换
.excludeFieldsWithoutExposeAnnotation()
// 使用自定义适配器
.registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory())
.registerTypeAdapter(UserDto.class,new UserDeserializer())
.registerTypeAdapter(WorkOrderDto.class,new WorkOrderDeserializer())
.create();
private String jsonPrefix;
public MyHttpMessagerCovert() {
super();
super.setGson(this.gson);
}
@Override
public void setJsonPrefix(String jsonPrefix) {
this.jsonPrefix = jsonPrefix;
}
@Override
public void setPrefixJson(boolean prefixJson) {
this.jsonPrefix = prefixJson ? ")]}', " : null;
}
@Override
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return super.read(type, contextClass, inputMessage);
}
@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return super.readInternal(clazz, inputMessage);
}
/**
* @deprecated
*/
@Override
protected TypeToken<?> getTypeToken(Type type) {
return super.getTypeToken(type);
}
@Override
protected void writeInternal(Object o, Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
Charset charset = this.getCharset(outputMessage.getHeaders());
OutputStreamWriter writer = new OutputStreamWriter(outputMessage.getBody(), charset);
try {
if (this.jsonPrefix != null) {
writer.append(this.jsonPrefix);
}
if (type != null) {
this.gson.toJson(o, type, writer);
} else {
this.gson.toJson(o, writer);
}
writer.close();
} catch (JsonIOException var7) {
throw new HttpMessageNotWritableException("Could not write JSON: " + var7.getMessage(), var7);
}
}
@Override
protected boolean supports(Class<?> clazz) {
return super.supports(clazz);
}
@Override
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
return super.canRead(type, contextClass, mediaType);
}
@Override
public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
return super.canWrite(type, clazz, mediaType);
}
Charset getCharset(HttpHeaders headers) {
return headers != null && headers.getContentType() != null
&& headers.getContentType().getCharset() != null ? headers.getContentType().getCharset()
: DEFAULT_CHARSET;
}
@Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
super.writeInternal(o, outputMessage);
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return super.getSupportedMediaTypes();
}
@Override
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
super.setSupportedMediaTypes(supportedMediaTypes);
}
@Override
public Charset getDefaultCharset() {
return super.getDefaultCharset();
}
@Override
public void setDefaultCharset(Charset defaultCharset) {
super.setDefaultCharset(defaultCharset);
}
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return super.canRead(clazz, mediaType);
}
@Override
protected boolean canRead(MediaType mediaType) {
return super.canRead(mediaType);
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return super.canWrite(clazz, mediaType);
}
@Override
protected boolean canWrite(MediaType mediaType) {
return super.canWrite(mediaType);
}
@Override
protected void addDefaultHeaders(HttpHeaders headers, Object o, MediaType contentType)
throws IOException {
super.addDefaultHeaders(headers, o, contentType);
}
@Override
protected MediaType getDefaultContentType(Object o) throws IOException {
return super.getDefaultContentType(o);
}
@Override
protected Long getContentLength(Object o, MediaType contentType) throws IOException {
return super.getContentLength(o, contentType);
}
/**
* Returns a hash code value for the object. This method is supported for the benefit of hash
* tables such as those provided by {@link HashMap}. <p> The general contract of {@code hashCode}
* is: <ul> <li>Whenever it is invoked on the same object more than once during an execution of a
* Java application, the {@code hashCode} method must consistently return the same integer,
* provided no information used in {@code equals} comparisons on the object is modified. This
* integer need not remain consistent from one execution of an application to another execution of
* the same application. <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of the two objects must produce the
* same integer result. <li>It is <em>not</em> required that if two objects are unequal according
* to the {@link Object#equals(Object)} method, then calling the {@code hashCode} method on each
* of the two objects must produce distinct integer results. However, the programmer should be
* aware that producing distinct integer results for unequal objects may improve the performance
* of hash tables. </ul> <p> As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct objects. (This is typically
* implemented by converting the internal address of the object into an integer, but this
* implementation technique is not required by the Java™ programming language.)
*
* @return a hash code value for this object.
* @see Object#equals(Object)
* @see System#identityHashCode
*/
@Override
public int hashCode() {
return super.hashCode();
}
/**
* Indicates whether some other object is "equal to" this one. <p> The {@code equals} method
* implements an equivalence relation on non-null object references: <ul> <li>It is
* <i>reflexive</i>: for any non-null reference value {@code x}, {@code x.equals(x)} should return
* {@code true}. <li>It is <i>symmetric</i>: for any non-null reference values {@code x} and
* {@code y}, {@code x.equals(y)} should return {@code true} if and only if {@code y.equals(x)}
* returns {@code true}. <li>It is <i>transitive</i>: for any non-null reference values {@code x},
* {@code y}, and {@code z}, if {@code x.equals(y)} returns {@code true} and {@code y.equals(z)}
* returns {@code true}, then {@code x.equals(z)} should return {@code true}. <li>It is
* <i>consistent</i>: for any non-null reference values {@code x} and {@code y}, multiple
* invocations of {@code x.equals(y)} consistently return {@code true} or consistently return
* {@code false}, provided no information used in {@code equals} comparisons on the objects is
* modified. <li>For any non-null reference value {@code x}, {@code x.equals(null)} should return
* {@code false}. </ul> <p> The {@code equals} method for class {@code Object} implements the most
* discriminating possible equivalence relation on objects; that is, for any non-null reference
* values {@code x} and {@code y}, this method returns {@code true} if and only if {@code x} and
* {@code y} refer to the same object ({@code x == y} has the value {@code true}). <p> Note that
* it is generally necessary to override the {@code hashCode} method whenever this method is
* overridden, so as to maintain the general contract for the {@code hashCode} method, which
* states that equal objects must have equal hash codes.
*
* @param obj the reference object with which to compare.
* @return {@code true} if this object is the same as the obj argument; {@code false} otherwise.
* @see #hashCode()
* @see HashMap
*/
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
/**
* Creates and returns a copy of this object. The precise meaning of "copy" may depend on the
* class of the object. The general intent is that, for any object {@code x}, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression: <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be {@code true}, but these are not absolute requirements. While it is typically the case
* that: <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be {@code true}, this is not an absolute requirement. <p> By convention, the returned
* object should be obtained by calling {@code super.clone}. If a class and all of its
* superclasses (except {@code Object}) obey this convention, it will be the case that {@code
* x.clone().getClass() == x.getClass()}. <p> By convention, the object returned by this method
* should be independent of this object (which is being cloned). To achieve this independence, it
* may be necessary to modify one or more fields of the object returned by {@code super.clone}
* before returning it. Typically, this means copying any mutable objects that comprise the
* internal "deep structure" of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only primitive fields or references
* to immutable objects, then it is usually the case that no fields in the object returned by
* {@code super.clone} need to be modified. <p> The method {@code clone} for class {@code Object}
* performs a specific cloning operation. First, if the class of this object does not implement
* the interface {@code Cloneable}, then a {@code CloneNotSupportedException} is thrown. Note that
* all arrays are considered to implement the interface {@code Cloneable} and that the return type
* of the {@code clone} method of an array type {@code T[]} is {@code T[]} where T is any
* reference or primitive type. Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of the corresponding fields of
* this object, as if by assignment; the contents of the fields are not themselves cloned. Thus,
* this method performs a "shallow copy" of this object, not a "deep copy" operation. <p> The
* class {@code Object} does not itself implement the interface {@code Cloneable}, so calling the
* {@code clone} method on an object whose class is {@code Object} will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not support the {@code Cloneable}
* interface. Subclasses that override the {@code clone} method can also throw this exception to
* indicate that an instance cannot be cloned.
* @see Cloneable
*/
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* Returns a string representation of the object. In general, the {@code toString} method returns
* a string that "textually represents" this object. The result should be a concise but
* informative representation that is easy for a person to read. It is recommended that all
* subclasses override this method. <p> The {@code toString} method for class {@code Object}
* returns a string consisting of the name of the class of which the object is an instance, the
* at-sign character `{@code @}', and the unsigned hexadecimal representation of the hash code of
* the object. In other words, this method returns a string equal to the value of: <blockquote>
* <pre>
* getClass().getName() + '@' + Integer.toHexString(hashCode())
* </pre></blockquote>
*
* @return a string representation of the object.
*/
@Override
public String toString() {
return super.toString();
}
/**
* Called by the garbage collector on an object when garbage collection determines that there are
* no more references to the object. A subclass overrides the {@code finalize} method to dispose
* of system resources or to perform other cleanup. <p> The general contract of {@code finalize}
* is that it is invoked if and when the Java™ virtual machine has determined that there is
* no longer any means by which this object can be accessed by any thread that has not yet died,
* except as a result of an action taken by the finalization of some other object or class which
* is ready to be finalized. The {@code finalize} method may take any action, including making
* this object available again to other threads; the usual purpose of {@code finalize}, however,
* is to perform cleanup actions before the object is irrevocably discarded. For example, the
* finalize method for an object that represents an input/output connection might perform explicit
* I/O transactions to break the connection before the object is permanently discarded. <p> The
* {@code finalize} method of class {@code Object} performs no special action; it simply returns
* normally. Subclasses of {@code Object} may override this definition. <p> The Java programming
* language does not guarantee which thread will invoke the {@code finalize} method for any given
* object. It is guaranteed, however, that the thread that invokes finalize will not be holding
* any user-visible synchronization locks when finalize is invoked. If an uncaught exception is
* thrown by the finalize method, the exception is ignored and finalization of that object
* terminates. <p> After the {@code finalize} method has been invoked for an object, no further
* action is taken until the Java virtual machine has again determined that there is no longer any
* means by which this object can be accessed by any thread that has not yet died, including
* possible actions by other objects or classes which are ready to be finalized, at which point
* the object may be discarded. <p> The {@code finalize} method is never invoked more than once by
* a Java virtual machine for any given object. <p> Any exception thrown by the {@code finalize}
* method causes the finalization of this object to be halted, but is otherwise ignored.
*
* @throws Throwable the {@code Exception} raised by this method
* @jls 12.6 Finalization of Class Instances
* @see WeakReference
* @see PhantomReference
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
}
}
- StringNullAdapter类实现
/**
* Package com.xqxf.utils
*
* author liyaocai
*
* date 2017/10/1 15:22
*
* Description:
*
* version V1.0
*/
public class StringNullAdapter extends TypeAdapter<String> {
@Override
public String read(JsonReader reader) throws IOException {
// TODO Auto-generated method stub
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return "";
}
return reader.nextString();
}
@Override
public void write(JsonWriter writer, String value) throws IOException {
// TODO Auto-generated method stub
if (value == null) {
writer.value("");
return;
}
writer.value(value);
}
}
- NullStringToEmptyAdapterFactory类实现
/**
* Package com.xqxf.utils
*
* author liyaocai
*
* date 2017/10/1 15:23
*
* Description:
*
* version V1.0
*/
public class NullStringToEmptyAdapterFactory<T> implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<T> rawType = (Class<T>) type.getRawType();
if (rawType != String.class) {
return null;
}
return (TypeAdapter<T>) new StringNullAdapter();
}
}