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)支持输出nullGson默认是不会输出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的使用大概就这么多内容,如果后续有更新就再补充。
相关阅读
Gson to json 的时候,出现nameValuePair
Gson 是一个很好的操作json的工具,无论是解析还是转换都是十分方便的。最近在开发中遇到一个需求,客户端从服务器端获取到数据,但是
Gson介绍:GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个Json字符转成一个Java对象,或者将一个Ja
Gson介绍: GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个Json字符转成一个Java对象,或者将一个
今天在我是设计师QQ群中:看到竟然有人公开叫卖www.lusongsong.cn域名。域名注册信息:Domain Name: lusongsong.cnROID: 20090929s10