解决Gson序列化实体类属性为null时返回null

问题描述: 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&trade; 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&trade; 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();
  }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 1.概述2.Gson的目标3.Gson的性能和扩展性4.Gson的使用者5.如何使用Gson 通过Maven来使用...
    人失格阅读 14,192评论 2 18
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,386评论 25 707
  • 欢迎Follow我的GitHub, 关注我的简书. 本文的合集已经编著成书,高级Android开发强化实战,欢迎各...
    SpikeKing阅读 493评论 1 2
  • 娘十九岁离开了姥姥,坐在花轿被耿家的近房抬到了娘一生都没有离开半步的村庄耿河湾。和我老实善良的父亲拜堂了。闹房时候...
    永无休止阅读 156评论 0 1