Gson使用指南

官方指南:https://github.com/google/gson/blob/master/UserGuide.md

本文主要是官方指南的简化版翻译,并加入了自己的测试用例,如果有能力最好还是参考官方指南。


一 Gson是什么?

Gson是一个可以将Java对象序列化成JSON表示,也可以将JSON字符串转化成Java对象的Java库。


二 Gson怎么用?

Android项目要在Gradle中使用Gson,需要在build.gradle(app)中进行如下配置:

dependencies {
    compile 'com.google.code.gson:gson:2.8.4'
}


1)序列化和反序列化基本数据类型:

// 序列化
Gson gson = new Gson();    //创建默认Gson对象
gson.toJson(1);            // ==> 1
gson.toJson("abcd");       // ==> "abcd"
gson.toJson(new Long(10)); // ==> 10
int[] values = { 1 };
gson.toJson(values);       // ==> [1]

// 反序列化
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);


2)序列化和反序列化Java对象:

public class GsonA {
    private String name;
    private String telephone;
    private int age;
    private int gender;
    private boolean married;
  public GsonA(String name, String phone, int age, int gender, boolean married) {
    this.name = name;
    this.telephone = phone;
    this.age = age;
    this.gender = gender;
    this.married = married;
}
}

序列化

Gson gson = new Gson();
GsonA a = new GsonA("klp", "136xxxxxxxx", 28, 1, true);
String jsonString = gson.toJson(a);
打印结果:
{"name":"klp","telephone":"136xxxxxxxx","age":28,"gender":1,"married":true}

注意带参构造方法不是必须的,这里只是为了方便测试。

反序列化

Gson json = new Gson();
String jsonString = "{\"name\":\"klp\",\"telephone\":\"136xxxxxxxx\",\"age\":28,\"gender\":1,\"married\":true}";
GsonA desA = gson.fromJson(jsonString, GsonA.class);

序列化和反序列化时以下几点需要注意:

    1、反序列化Json串时,要反序列化的对象不可以有循环引用,如下:

class A {
    B b;
}

class B {
    A a;
}

    2、private类型的属性也可以被序列化和反序列化

    3、在序列化和反序列化时,本类中以及父类中所有的属性都会被包含在内;

    4、如果某个属性是transient修饰的,那么这个字段默认不会被序列化和反序列化;

    5、Gson对可以正确的操作null,在序列化时,如果某个对象属性为null,默认会忽略这个属性,即序列化后的字符串中不会有"name":null;在反序列化时,如果某个对象属性在JSON串中没有对应的值,那么这个属性会被赋值为null。

    6、对于Java基本类型的属性,在反序列化时如果JSON串中没有对应的字段,那么它们就会被赋值默认值,比如int型属性会赋值0,boolean属性会赋值false。

    7、synthetic属性在序列化和反序列化时会被忽略。(synthetic属性不是指其他类对象的引用,而是Java在编译过程中引入的属性,我们一般不会遇到,关于synthetic请自行百度。)

    8、内部类、匿名内部类、本地类中对应外部类的字段在序列化和饭序列化过程中也会被忽略。


3)序列化和反序列化内部类

对于静态内部类Gson可以轻松的进行序列化和反序列化,其过程和上面序列化Java对象是一样的。对于非匿名内部类,官方指导说Gson默认无法正常反序列化,原因是在反序列化时,内部类的构造方法需要通过外部类的实例调用,但在反序列化时还没有外部类的实例,所以无法反序列化,但是在我测试过程中是可以正常反序列化的,下面是我的代码和打印结果:

public class ModelA {
    public class NestedA {
        public String nestedA;
    }
}
Gson gson = new Gson();
String nestedString = "{\"nestedA\":\"nestedTest\"}";
ModelA.NestedA nestedA = gson.fromJson(nestedString, ModelA.NestedA.class);
Log.i("GsonActivity", "nestedA: " + nestedA.nestedA);
打印结果:GsonActivity: nestedA: nestedTest

当然官方也给出了解决方案:

1、将内部类改成静态内部类;

2、自定义InstanceCreator,然后用这个Creator创建Gson实例(这种方式不推荐!!!),下面是代码实例:

public class InstanceCreatorForNestedA implements InstanceCreator<ModelA.NestedA> {
    private ModelA a;
    
    public InstanceCreatorForNestedA(ModelA modelA) {
        a = modelA;
    }
    
    @Override
    public ModelA.NestedA createInstance(Type type) {
        return a.new NestedA();
    }
}
//使用自定义的InstanceCreator反序列化NestedA
GsonBuilder builder = new GsonBuilder();        
builder.registerTypeAdapter(ModelA.NestedA.class, new InstanceCreatorForNestedA(new ModelA()));
Gson bg = builder.create();
ModelA.NestedA nestedA = bg.fromJson(nestedString, ModelA.NestedA.class);
Log.i("GsonActivity", "nestedA: " + nestedA.nestedA);
打印结果:nestedA: nestedTest

可以看到这种方式是可行的,但是不推荐!!!


4)序列化和反序列化数组

Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"klp", "wjj", "jxl"};

// 序列化
gson.toJson(ints);     // ==> [1,2,3,4,5]
gson.toJson(strings);  // ==> ["klp", "wjj", "jxl"]

// 反序列化
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); 
// 可以看到ints2和ints1是一样的

除了基本类型的数组,也可以序列化和反序列化其他类型的数组。


5)序列化和反序列化集合类
//序列化
Gson gson = new Gson();
List<Integer> intList = new ArrayList<>();
for(int i = 1; i <= 5; i++) {
   intList.add(i);
}
String JSONString = gson.toJson(intList, List.class);
Log.i("GsonActivity", JSONString);

//反序列化
//因为TypeToken的构造方法是用protected修饰的,所以我们必须创建它的子类
TypeToken type = new TypeToken<List<Integer>>(){};
List<Integer> intList2 = gson.fromJson(JSONString, type.getType());
Log.i("GsonActivity", intList2.toString());
打印结果:
GsonActivity: [1,2,3,4,5]
GsonActivity: [1, 2, 3, 4, 5]
注意:在反序列化集合时,我们必须要注明集合中的子类型是什么,否则Gson是不可能知道的,要注明集合的子类型,我们就需要创建TypeToken的子类的对象,如上面代码。

6)序列化和反序列化泛型

/**
 * 定义一个泛型类
 */
public class Model<T> {
    public String name;
    public T value;

    public Model(String name, T value) {
        this.name = name;
        this.value = value;
    }
}
public class ModelA {
    public String value;

    public ModelA(String v) {
        value = v;
    }
}
//序列化
Model<ModelA> model1 = new Model("ModelA", new ModelA("a"));
Gson gson = new Gson();
String JSONString = gson.toJson(model1);
Log.i("GsonActivity", JSONString);

//反序列化
Type type = new TypeToken<Model<ModelA>>(){}.getType();
Model<ModelA> model2 = gson.fromJson(JSONString, type);
Log.i("GsonActivity", model2.name + " " + model2.value.value);
打印结果:
{"name":"ModelA","value":{"value":"a"}}
ModelA a

就像序列化和反序列化集合,Gson可以正常序列化泛型,但是在反序列化时必须告知Gson泛型指定了哪种具体类型,否则Gson是无法反序列化的。


7)序列化和反序列化包含多种类型的Collection

以List为例,List的创建如下: 

List list = new ArrayList();
list.add("hello");
list.add(1);
list.add(true);
list.add(new EventModel("klp", "test"));

可见List集合中包含String元素,也包含int型元素,还有boolean型和自定义的EventModel类型。(这种情况在我的开发过程中还没有遇到过,估计以后也会很少遇到。)

对于上述情况,序列化时不会有问题,但对于反序列化,Gson也很难处理,Gson提供了三种解决方案:

 1、使用JsonParser将其解析成一个个节点,然后各自反序列化,很显然这种方式比较麻烦,针对这种情况官方给出了一个实例:https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java

 2、为Collection.class对象注册适配器,这样就能为每一个成员映射一个合适的对象,但这种方式可能会将其他的Collection搞乱。下面是一段实例代码:

//自己定义的EventModel
public class EventModel {
    private String name;
    private String value;

    public EventModel(String name, String value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
/**
 * 解析有多种类型的List的适配器
 * Created by lupeng.kang on 18/5/17.
 */

public class ArbitraryAdapter extends TypeAdapter<List> {

    public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
        @SuppressWarnings("unchecked")
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
            return typeToken.getRawType() == List.class ? (TypeAdapter<T>) new ArbitraryAdapter() : null;
        }
    };

    @Override
    public void write(JsonWriter out, List value) throws IOException {
        if (value == null) {
            return;
        }
        for (int i = 0; i < value.size(); i++) {
            Object obj = value.get(i);
            if (obj == null) {
                out.nullValue();
            } else if (obj instanceof String) {
                out.value((String) obj);
            } else if (obj instanceof Number) {
                out.value((Number) obj);
            } else if (obj instanceof Boolean) {
                out.value((Boolean) obj);
            } else if (obj instanceof EventModel) {
                EventModel event = (EventModel) obj;
                out.beginObject();
                out.name("name");
                out.value(event.getName());
                out.name("value");
                out.value(event.getValue());
                out.endObject();
            } else if (obj instanceof List) {
                out.beginArray();
                write(out, (List) obj);
                out.endArray();
            } else {
                throw new IllegalArgumentException("Couldn't write " + value.getClass());
            }
        }
    }

    @Override
    public List read(JsonReader in) throws IOException {
        List collection = new ArrayList();
        switch (in.peek()) {
            case BEGIN_ARRAY:
                in.beginArray();
                while (in.hasNext()) {
                    collection.add(readObj(in));
                }
                in.endArray();
                break;
            case STRING:
            case NUMBER:
            case BOOLEAN:
            case NULL:
            case BEGIN_OBJECT:
            case END_OBJECT:
            case END_ARRAY:
            case END_DOCUMENT:
            case NAME:
            default:
                throw new IllegalArgumentException();
        }
        return collection;
    }

    private Object readObj(JsonReader in) throws IOException {
        switch (in.peek()) {
            case STRING:
                return in.nextString();
            case NUMBER:
                return new LazilyParsedNumber(in.nextString());
            case BOOLEAN:
                return in.nextBoolean();
            case NULL:
                in.nextNull();
                return null;
            case BEGIN_OBJECT:
                Map<String, String> map = new HashMap<>();
                List<String> nameList = new ArrayList<>();
                in.beginObject();
                while (in.hasNext()) {
                    String name = in.nextName();
                    nameList.add(name);
                    map.put(name, in.nextString());
                }
                in.endObject();
                EventModel event = new EventModel(map.get(nameList.get(0)), map.get(nameList.get(1)));
                return event;
            case BEGIN_ARRAY:
                in.beginArray();
                List list = read(in);
                in.endArray();
                return list;
            case END_ARRAY:
            case END_OBJECT:
            case END_DOCUMENT:
            case NAME:
            default:
                throw new IllegalArgumentException();
        }
    }

}
//向Gson注册我们自定义的适配器,并用这个Gson对象序列化和反序列化list
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapterFactory(ArbitraryAdapter.FACTORY);
Gson gson = builder.create();
//序列化
String json = gson.toJson(list);
Log.i("GsonActivity", json);

//反序列化
List list1 = gson.fromJson(json, List.class);
//打印List内容
if (list1 != null) {
    StringBuilder sBuilder = new StringBuilder();
    for (int i = 0; i < list1.size(); i++) {
        if (list1.get(i) instanceof EventModel) {
            EventModel model = (EventModel) list1.get(i);
            sBuilder.append("EvemtModel {");
            if (model != null) {
                sBuilder.append("name:");
                sBuilder.append(model.getName());
                sBuilder.append(" value:");
                sBuilder.append(model.getValue());
            }
            sBuilder.append("}");
        } else {
            sBuilder.append(list1.get(i)).append(" ");
        }
    }
    Log.i("GsonActivity", sBuilder.toString());
} else {
    Log.i("GsonActivity", "解析失败!");
}
    3、注册一个类型为MyCollectionType的适配器,在反序列化的时候使用,这种情况跟前面 反序列化集合的是一样的,但这种情况也有问题,那就是必须保证Json串表现为数组,而且你能够保证Json串表现为collection<MyCollectionType>,这个时候才能用这种方式。


对于这种一个集合中有多种类型的元素的情况是比较少见的,个人感觉在开发过程中也应该尽量避免这种数据结构。


三 Gson的特殊功能

Gson对于数据的序列化和反序列化大概就这么多,但是Gson的功能绝不止这些,下面还有一些非常有用的功能需要了解。

1)Gson输出不同格式的Json串

    默认情况下,Gson输出的Json串是比较紧凑的,正如前面例子中打印出的样子,它会去掉所有的空格,也会去掉为空的字段,并且会在一行上显示。当然Gson也可以输出“漂亮”的格式,如果要输出漂亮的格式,就需要使用GsonBuilder进行配置,代码如下:

GsonA a = new GsonA("klp", "136xxxxxxxx", 28, 1, true);

Gson gson = new Gson();
Log.i("GsonActivity", gson.toJson(a));

GsonBuilder builder = new GsonBuilder();
builder.setPrettyPrinting();
Gson bg = builder.create();
Log.i("GsonActivity", bg.toJson(a));
//打印结果:
//紧凑格式
{"age":28,"gender":1,"married":true,"name":"klp","telephone":"136xxxxxxxx"}
//漂亮格式
{
  "age": 28,
  "gender": 1,
  "married": true,
  "name": "klp",
  "telephone": "136xxxxxxxx"
}
2)支持输出null

    Gson默认是不会输出null的,如果要输出null就要进行如下设置:

Gson bg = new GsonBuilder().serializeNulls().create();

3)版本支持

    某些类可能会用@Since注解来维护一个对象的多个版本,为了利用这个功能,可以配置Gson对象,使其序列化时忽略某个版本之后的所有字段。默认情况下,Gson序列化时会保留所有字段。    

public class GsonA {

    private String name;
    private String telephone;
    @Since(1.0)
    private int age;
    @Since(2.0)
    private int gender;
    private boolean married;

    public GsonA() {
        name = "klp";
        telephone = "136xxxxxxxx";
        age = 28;
        gender = 1;
        married = true;
    }
}
GsonA a = new GsonA();
//默认序列化
Gson gson = new Gson();
Log.i("GsonActivity", gson.toJson(a));
//版本支持
son = new GsonBuilder().setVersion(1.0).create();
Log.i("GsonActivity", bg.toJson(a));
//打印结果
{"age":28,"gender":1,"married":true,"name":"klp","telephone":"136xxxxxxxx"}
{"age":28,"married":true,"name":"klp","telephone":"136xxxxxxxx"}
4)Gson支持序列化和反序列化时忽略一些字段

    默认情况下,static和transient修饰的字段在序列化和反序列化时都会被忽略,但是Gson提供了以下方式忽略某些字段的策略:

    1、使用GsonBUilder的excludeFieldsWithModifiers()方法

Gson bg = new GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create();
    使用上述配置,static修饰的字段将会被序列化。当然这个方法可以传多个参数,如下:
Gson bg = new GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC).create();

    这样static和transient修饰的属性都会被忽略。

    2、使用@Expose注解

    先看段代码:

public class GsonA {
    @Expose
    private String name;
    private static String telephone;

    public GsonA() {
        name = "klp";
        telephone = "136xxxxxxxx";
    }
}
GsonA a = new GsonA();
//使用@expose注解
Gson bg = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Log.i("GsonActivity", bg.toJson(a));
打印结果:{"name":"klp"}

    可以看到没有使用注解的属性会被忽略。

    3)自定义排除策略插入到Gson

/**
 * 测试自定义排除策略的Model
 */
public class ExclusionModel {
    
    //自定义注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD})
    public @interface Foo {}
    //使用自定义注解
    @Foo private final int annotationedField;
    private final String name;
    private final int age;

    public ExclusionModel() {
        annotationedField = 1;
        name = "klp";
        age = 1;
    }

    public String string() {
        StringBuilder builder = new StringBuilder();
        builder.append("annotationedField:").append(annotationedField).append(" name:").append(name)
                .append(" age:").append(age);
        return builder.toString();
    }
}
/**
 * 自定义序列化排除策略
 *
 * Created by lupeng.kang on 18/5/16.
 */

public class MyExclusionStratigy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(ExclusionModel.Foo.class) != null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return clazz == String.class;
    }
}
//使用自定义排除策略序列化对象
Gson gson = new GsonBuilder().serializeNulls().setExclusionStrategies(new MyExclusionStratigy()).create();
String exJson = gson.toJson(new ExclusionModel());
Log.i("GsonActivity", exJson);
打印结果:{"age":1}
使用自定义注解注释的name属性被忽略了。

4)序列化和饭序列化时修改字段名称

    这个是一个比较常用的功能,由于客户端和服务端定义的字段名不一样,可能需要用到这个功能。要使用这个功能有两种方法,

一是使用Gson定义好的策略,比如:

Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASH).create();

第二种是使用注解

public class RenameTest {
    @SerializedName("custem_name")
    private String customName;
}

关于Gson的使用大概就这么多内容,如果后续有更新就再补充。




猜你喜欢

转载自blog.csdn.net/kanglupeng/article/details/80347788