Gson系列2 --- 番外篇 -- Gson如何序列化值为null的属性为空字符串或null

1、简述

> 番外篇 如何序列化值为null的属性
  
  方式1new GsonBuilder().serializeNulls().create();
  
    方式2: 重写相应的 TypeAdapters 解析规则 -- null 转换为空字符串
  
  方式3: 自定义 TypeAdapterFactory 
下面重点介绍方式2 和 方式3

2、方式2 重写TypeAdapters的解析规则

  

 通过new Gson()查看对应的构造器 ,
    Gson的数据解析都是委托到各个TypeAdapter内进行处理的。
    在Gson的构造函数内会预先加载一部分TypeAdapter,
       包含Stringintlongdouble等类型,都存放在factories中,如下:
       
发现都可以通过自定义TypeAdapter解决。

自定义TypeAdapter,将其放入facotries中,
  并且gson在解析json时使用对应的TypeAdapter来的,
  而我们手动添加的TypeAdapter会优先于预设的TypeAdapter被使用
  
因此 我们来覆盖 一些常用的包装类型的 null值的序列化

重写自己的TypeAdapters  

package sun.rain.amazing.gson.type;

import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;

/**
 * 变成不可被继承的
 * @author sunRainAmazing
 */
public final class TypeAdaptersRedefine {

    public static final String EMPTY = "";


    /**
     * 对于String 类型 的 策略
  */
    public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
        //进行反序列化
        @Override
        public String read(JsonReader reader) {
            try {
                if (reader.peek() == JsonToken.NULL) {
                    reader.nextNull();
                    return null;
                }
                //要进行属性值的判断 若为 空字符串 则返回null 否则返回 本身的值
        String result = reader.nextString();
                return result.length() > 0 ? result : null;
            } catch (Exception e) {
                throw new JsonSyntaxException(e);
            }
        }

        // 进行序列化
        @Override
        public void write(JsonWriter writer, String value) {
            try {
                if (value == null) {
                    writer.value(EMPTY);
                    return;
                }
                writer.value(value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };


    /**
     * 对于int Integer 类型
  * 由于 int 类型 有默认值 0  *    -- 通常我们无法确定 0 是否具备实际意义
  * 但是 Integer 的类型 null , 我们可以确定的是 -- 无意义的
  *
    * 因此在设计属性的类型是 通常采用 Integer  而不是 int 类型
  *
   * 故 由于 int 0  具备 实际意义  -- 不进行转换
  * 而是转换 Integer 类型的 null   */
    public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() {
        @Override
        public Number read(JsonReader in) throws IOException {
            System.out.println(in.peek() +" ----->");
            if (in.peek() == JsonToken.NULL) {
                in.nextNull();
                return null;
            }
            if (in.peek() == JsonToken.STRING) {
                in.nextString();
                return null;
            }
            return in.nextInt();
        }
        @Override
        public void write(JsonWriter out, Number value) throws IOException {
            if(value == null){
                out.value(EMPTY);
            }else{
                out.value(value);
            }
        }
    };

    /**
     * 对于double类型的转换
  */
    public static final TypeAdapter<Number> DOUBLE = new TypeAdapter<Number>() {
        @Override
        public Number read(JsonReader in) throws IOException {
            if (in.peek() == JsonToken.NULL) {
                in.nextNull();
                return null;
            }
            if (in.peek() == JsonToken.STRING) {
                in.nextString();
                return null;
            }
            return in.nextDouble();
        }
        @Override
        public void write(JsonWriter out, Number value) throws IOException {
            if(value == null){
                out.value(EMPTY);
            }else{
                out.value(value);
            }
        }
    };


}

相关测试类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GsonStrNull1 {
    private String str;
    private Integer integer;
    private Double doubleType;
    private int intType;

    public GsonStrNull1(String str) {
        this.str = str;
    }
    public GsonStrNull1(String str, Integer integer) {
        this.str = str;
        this.integer = integer;
    }
    public GsonStrNull1(String str, Integer integer, Double doubleType) {
        this.str = str;
        this.integer = integer;
        this.doubleType = doubleType;
    }

    public GsonStrNull1(Integer integer, Double doubleType, int intType) {
        this.integer = integer;
        this.doubleType = doubleType;
        this.intType = intType;
    }

    public GsonStrNull1(Double doubleType, int intType) {
        this.doubleType = doubleType;
        this.intType = intType;
    }
    public GsonStrNull1(Double doubleType) {
        this.doubleType = doubleType;
    }
}

测试类

  

package sun.rain.amazing.gson.strnull;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.junit.Test;
import sun.rain.amazing.gson.stringnull.GsonStrNull;
import sun.rain.amazing.gson.stringnull.GsonStrNull1;
import sun.rain.amazing.gson.stringnull.NullStringToEmptyAdapterFactory;
import sun.rain.amazing.gson.type.TypeAdaptersRedefine;

/**
 * @author Reese
 */
public class GsonTypeAdaptersNullTest {

    /**
     * {"str":"tom","integer":12,"doubleType":12.56,"intType":0}
     * GsonStrNull1(str=tom, integer=12, doubleType=12.56, intType=0)
     */
    @Test
    public void testNullAll(){
        GsonStrNull1 g = new GsonStrNull1("tom",12,12.56);
        Gson gson  = new GsonBuilder()
                // 进行注册自己的 TypeAdapter
                .registerTypeAdapter(String.class,TypeAdaptersRedefine.STRING)
                .registerTypeAdapter(Integer.class,TypeAdaptersRedefine.INTEGER)
                .registerTypeAdapter(Double.class,TypeAdaptersRedefine.DOUBLE)
                .create();
        //序列化
    String json = gson.toJson(g);
        System.out.println(json);
        //反序列化
    g = gson.fromJson(json, GsonStrNull1.class);
        System.out.println(g);
    }


    @Test
    public void testNullIntegerAndDouble(){
        GsonStrNull1 g = new GsonStrNull1("tom");
        Gson gson  = new GsonBuilder()
                // 进行注册自己的 TypeAdapter
                .registerTypeAdapter(String.class,TypeAdaptersRedefine.STRING)
                .registerTypeAdapter(Integer.class,TypeAdaptersRedefine.INTEGER)
                .registerTypeAdapter(Double.class,TypeAdaptersRedefine.DOUBLE)
                .create();
        //序列化
    String json = gson.toJson(g);
        System.out.println(json);
        //反序列化
    g = gson.fromJson(json, GsonStrNull1.class);
        System.out.println(g);
    }

    @Test
    public void testNullFloat(){
        GsonStrNull1 g = new GsonStrNull1("tom",12);
        Gson gson  = new GsonBuilder()
                // 进行注册自己的 TypeAdapter
                .registerTypeAdapter(String.class,TypeAdaptersRedefine.STRING)
                .registerTypeAdapter(Integer.class,TypeAdaptersRedefine.INTEGER)
                .registerTypeAdapter(Double.class,TypeAdaptersRedefine.DOUBLE)
                .create();
        //序列化
    String json = gson.toJson(g);
        System.out.println(json);
        //反序列化
    json = "{\"str\":\"tom\",\"integer\":12.3}";
        g = gson.fromJson(json, GsonStrNull1.class);
        System.out.println(g);
    }


}


3、方式3 自定义 TypeAdapterFactory 

针对String 类型

package sun.rain.amazing.gson.stringnull;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
/**
 *  仅针对于  String 类型的数据
 * @author sunRainAmazing
 */
public class StringNullAdapter extends TypeAdapter<String> {
    /**
     * 反序列化时 若为 null 或 空字符串
   *    皆反序列化 为 null
     * @param reader
     * @return
     * @throws IOException
     */
    @Override
    public String read(JsonReader reader) throws IOException {

        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        }
        String res = reader.nextString();
        return "".equals(res) ? null : res;
    }

    /**
     * 序列化时  若属性值 为null  则 序列化为 空字符串
   * 反之  反序列化时 若属性值 为 null 或者为 空字符串时  --
     *    则对应反序列化为 null
     * @param writer
     * @param value
     * @throws IOException
     */
    @Override
    public void write(JsonWriter writer, String value) throws IOException {
        if (value == null) {
            writer.value("");
            return;
        }
        writer.value(value);
    }
}

创建适配器工厂

package sun.rain.amazing.gson.stringnull;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;

/**
 * @author sunRainAmazing
 */
public class NullStringToEmptyAdapterFactory implements TypeAdapterFactory {
    @SuppressWarnings("unchecked")
    @Override
    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();
    }
}

测试类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GsonStrNull {
    private String name;
    private String email;
    private Integer id;

    public GsonStrNull(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public GsonStrNull(String name) {
        this.name = name;
    }

    public GsonStrNull(String name, Integer id) {
        this.name = name;
        this.id = id;
    }
}

package sun.rain.amazing.gson.strnull;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.junit.Test;
import sun.rain.amazing.gson.stringnull.GsonObjNull;
import sun.rain.amazing.gson.stringnull.GsonStrNull;
import sun.rain.amazing.gson.stringnull.NullStringToEmptyAdapterFactory;
import sun.rain.amazing.gson.stringnull.NullToEmptyAdapterFactory;

/**
 * @author sunRainAmazing
 */
public class GsonStringTypeAdapterFactoryTest {

    /**
     * {"name":"tom","email":"","id":12}
     * GsonStrNull(name=tom, email=null, id=12)
     */
    @Test
    public void testNull(){
        GsonStrNull g = new GsonStrNull("tom",12);
        Gson gson  = new GsonBuilder()
                                        // 进行注册自定义的适配器工厂
                    .registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory())
                                        .create();
        //序列化
    String json = gson.toJson(g);
        System.out.println(json);
        //反序列化
    g = gson.fromJson(json, GsonStrNull.class);
        System.out.println(g);
    }
}

下面进一步封装 

针对于String 、 Integer 、 Double 、Boolean类型

package sun.rain.amazing.gson.stringnull;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;

/**
 *  对于反序列化而言 这里 仅支持
 *  String
 *  int / Integer
 *  Double /double
 *  Boolean / boolean
 *
 *
 * @author sunRainAmazing
 */
public class BaseNullAdapter<T> extends TypeAdapter<Object> {
    /**
     * 反序列化时 若为 null 或 空字符串
  *    皆反序列化 为 null
     * @param reader
     * @return
     * @throws IOException
     */
    @Override
    @SuppressWarnings("unchecked")
    public Object read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        }
        // 这里仅针对与 Integer  Double类型
    if (reader.peek() == JsonToken.NUMBER) {
            double res = reader.nextDouble();
            if(Math.floor(res) == res){
                return ((int) res);
            }else{
                return res;
            }
        }
        if (reader.peek() == JsonToken.BOOLEAN) {
            return reader.nextBoolean();
        }

        String res = reader.nextString();
        return "".equals(res) ? null : res;
    }

    /**
     * 序列化时  若属性值 为null  则 序列化为 空字符串
  * 反之  反序列化时 若属性值 为 null 或者为 空字符串时  --
     *    则对应反序列化为 null
     * @param writer
     * @param value
     * @throws IOException
     */
    @Override
    public void write(JsonWriter writer, Object value) throws IOException {
        if (value == null) {
            writer.value("");
            return;
        }
        // 在 反序列化 是 无法动态 绑定其类型
   if(value instanceof Number){
            writer.value((Number) value);
            return;
        }
        if(value instanceof Boolean){
            writer.value((Boolean) value);
            return;
        }

        writer.value(value.toString());
    }
}
package sun.rain.amazing.gson.stringnull;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;

/**
 * @author sunRainAmazing
 */
public class NullToEmptyAdapterFactory implements TypeAdapterFactory {
    @SuppressWarnings("unchecked")
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        Class<T> rawType = (Class<T>) type.getRawType();
        if (!isRequiredType(rawType)) {
            return null;
        }
        return (TypeAdapter<T>) new BaseNullAdapter<T>();
    }

    private boolean isRequiredType(Class cla){
        return cla == int.class || cla == Integer.class
                || cla == String.class
                || cla == double.class || cla == Double.class
                || cla == boolean.class || cla == Boolean.class;
    }
}

测试类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GsonObjNull {
    private String name;
    private Integer integerType;
    private int in;
    private Double doubleType;
    private double dou;
    private Boolean boolType;
    private boolean bool;

    private Character ch;

    public GsonObjNull(String name, Character ch) {
        this.name = name;
        this.ch = ch;
    }
}
package sun.rain.amazing.gson.strnull;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.junit.Test;
import sun.rain.amazing.gson.stringnull.GsonObjNull;
import sun.rain.amazing.gson.stringnull.NullToEmptyAdapterFactory;

/**
 * @author sunRainAmazing
 */
public class GsonTypeAdapterFactoryTest {


    /**
     * {"name":"tom","integerType":12,"in":10,
     * "doubleType":12.36,"dou":15.32,"boolType":true,"bool":true,"ch":"A"}
     *
     * GsonObjNull(name=tom, integerType=12, in=10,
     * doubleType=12.36, dou=15.32, boolType=true, bool=true, ch=A)
     */
    @Test
    public void testObjNull(){
        GsonObjNull g = new GsonObjNull("tom",12,10,
                12.36,15.32,true,true,'A');
        Gson gson  = new GsonBuilder()
                    .registerTypeAdapterFactory(new NullToEmptyAdapterFactory())
                .create();

        //序列化
        String json = gson.toJson(g);
        System.out.println(json);
        //反序列化
        g = gson.fromJson(json, GsonObjNull.class);
        System.out.println(g);
    }

    /**
     * {"name":"tom","integerType":"","in":0,"doubleType":"","dou":0.0,"boolType":"","bool":false,"ch":"A"}
     * GsonObjNull(name=tom, integerType=null, in=0, doubleType=null, dou=0.0, boolType=null, bool=false, ch=A)
     */
    @Test
    public void testObjNull1(){
        GsonObjNull g = new GsonObjNull("tom",'A');
        Gson gson  = new GsonBuilder()
                .registerTypeAdapterFactory(new NullToEmptyAdapterFactory())
                .create();

        //序列化
        String json = gson.toJson(g);
        System.out.println(json);
        //反序列化
        g = gson.fromJson(json, GsonObjNull.class);
        System.out.println(g);
    }

}
 
 

三种方式 各有优劣,

若无其他需求,  本人建议采用方式1




猜你喜欢

转载自blog.csdn.net/sunrainamazing/article/details/80952680