跳转至

Gson

Gson是Google开发的能够将对象转化为Json并且能够将Json转化为对象的Java库。

1.基本使用

使用Android Studio,我们可以使用Gradle进行构建:

compile 'com.google.code.gson:gson:2.7'

Gson提供了两个方法fromJson() 和toJson() 进行反序列化和序列化。

1.1基本数据类型

反序列化操作

1
2
3
4
5
Gson gson = new Gson();
gson.fromJson("abc", String.class);//abc
gson.fromJson("0123", int.class);//123
gson.fromJson("false", boolean.class);//false
gson.fromJson("0.123", float.class);//0.123
序列化操作
1
2
3
4
5
Gson gson = new Gson();
gson.toJson("abc");//"abc"
gson.toJson(0xa);//10
gson.toJson(false);//false
gson.toJson(0.123);//0.123

1.2对象

public String name;
public int age;
public String emailAddress;
public User(String name, int age, String emailAddress) {
    this.name = name;
    this.age = age;
    this.emailAddress = emailAddress;
}
@Override
public String toString() {
    return "User{" +
            "name='" + name + '\'' +
            ", age=" + age +
            ", emailAddress='" + emailAddress + '\'' +
            '}';
}

Gson gson = new Gson();
String json=gson.toJson(new User("mlk",26,"[email protected]"));
System.out.println(json);//{"name":"mlk","age":26,"emailAddress":"[email protected]"}
System.out.println(gson.fromJson(json,User.class));
//User{name='mlk', age=26, emailAddress='[email protected]'}
默认情况下,对象字段的名字和json的key是相同的。但某些时候给定的json的key的命名并不是我们想要的,比如给定的json字符串是这样的:
{"name":"mlk","age":26,"email_address":"[email protected]"}
可以看到email_address并不符合我们Java中的驼峰命名规则,这样我们需要借助一个注解@SerializedName来实现我们想要的结果。

@SerializedName用来指定json序列化的名字。

@SerializedName(value = "email_address")
public String emailAddress;

1
2
3
4
5
Gson gson = new Gson();
String json="{\"name\":\"mlk\",\"age\":26,\"email_address\":\"[email protected]\"}";
User user = gson.fromJson(json,User.class);
System.out.println(user);//User{name='mlk', age=26, emailAddress='[email protected]'}
System.out.println(gson.toJson(user));//{"name":"mlk","age":26,"email_address":"[email protected]"}
@SerializedName还提供了另外一个属性alternate 接收一个数组,当匹配到value值或者数组中的任意一个即可。
@SerializedName(value = "email",alternate = {"emailAddress","email_address"})
public String emailAddress;

1
2
3
4
5
6
7
Gson gson = new Gson();
String json="{\"name\":\"mlk\",\"age\":26,\"email\":\"[email protected]\"}";
User user = gson.fromJson(json,User.class);
System.out.println(user);//User{name='mlk', age=26, emailAddress='[email protected]'}
System.out.println(gson.toJson(user));
//注意这里序列化的结果
//{"name":"mlk","age":26,"email":"[email protected]"}
当多个同时出现,以最后一个为准。
1
2
3
4
5
6
Gson gson = new Gson();
String json="{\"name\":\"mlk\",\"age\":26,\"email\":\"[email protected]\",\"email_address\":\"[email protected]\"}";
User user = gson.fromJson(json,User.class);
System.out.println(user);//User{name='mlk', age=26, emailAddress='[email protected]'}
System.out.println(gson.toJson(user));
//{"name":"mlk","age":26,"email":"[email protected]"}

1.3数组的序列化和反序列化
1
2
3
4
5
6
7
Gson gson = new Gson();
String[] array = {"abc","def","hij"};
String json =gson.toJson(array);
System.out.println(json);
String[] array2 = gson.fromJson(json,String[].class);
System.out.println(Arrays.toString(array2));
//[abc, def, hij]
1.4 集合

使用集合,涉及到泛型我们就不能像上面一样gson.fromJson(json,List<String>.class)来解析json,因为对于Java来说List和List这俩个的字节码文件都是List.class。 为了解决的上面的问题,Gson为我们提供了TypeToken来实现对泛型的支持,所以当我们希望使用将以上的数据解析为List时需要这样写。

Gson gson = new Gson();
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("acb");
arrayList.add("def");
arrayList.add("ghi");
String json =gson.toJson(arrayList);//["acb","def","ghi"]
System.out.println(json);
//TypeToken的构造方法是protected修饰的 所以要这样写
List<String> list= gson.fromJson(json,new TypeToken<List<String>>(){}.getType());
System.out.println(list);
//[abc, def, hij]

2.流解析

Gson还支持流解析,通过这种方式不需要把整个json串加载到内存中,可以边加载边解析效率更高。

序列化:

String json = "{\"name\":\"mlk\",\"age\":27,\"emailAddress\":\"[email protected]\"}";
JsonReader jsonReader = new JsonReader(new StringReader(json));
jsonReader.beginObject();
String name=null;
int age=0;
String emailAddress=null;
while (jsonReader.hasNext()){
    switch (jsonReader.nextName()){
        case "name":
            name = jsonReader.nextString();
            break;
        case "age":
            age = jsonReader.nextInt();
            break;
        case "emailAddress":
            emailAddress = jsonReader.nextString();
            break;
    }
}
jsonReader.endObject();
System.out.println(new User(name,age,emailAddress));
//User{name='mlk', age=27, emailAddress='[email protected]'}
反序列化
1
2
3
4
5
6
7
8
JsonWriter writer = new JsonWriter(new OutputStreamWriter(System.out));
writer.beginObject()
        .name("name").value("mlk")
        .name("age").value(27)
        .name("emailAddress").nullValue()//
        .endObject();
writer.flush();
//{"name":"mlk","age":27,"emailAddress":null}

3.自定义配置

一般情况下Gson类提供的API已经能满足大部分的使用场景,但我们需要更多更特殊、更强大的功能时,这时候就引入一个新的类GsonBuilder。GsonBuilder用于构建一个自定义的Gson对象。

Gson gson = new GsonBuilder().create();

3.1Null

默认情况下,null值将被忽略。

1
2
3
Gson gson = new Gson();
User user = new User("mlk",27,null);
System.out.println(gson.toJson(user));//{"name":"mlk","age":27}
如果我们想要输出null的话,使用如下配置即可:
Gson gson = new GsonBuilder().serializeNulls().create();

3.2时间日期格式化

我们给User增加一个Date类型的字段birthday。

public Date birthday;
默认情况下进行序列化得到的字符串如下,代码就不给出了。
1
2
3
4
5
6
{
  "name": "mlk",
  "age": 27,
  "emailAddress": "[email protected]",
  "birthday": "Sep 1, 2016 2:08:22 PM"
}
如果我们想要得到birthday是我们自己想要的格式比yyyy年MM月dd日,要怎么做呢。GsonBuilder为我们提供了一个setDateFormat()方法,允许我们自己设置规则。
1
2
3
4
5
6
7
8
9
Gson gson = new GsonBuilder()
        .setDateFormat("yyyy年MM月dd日")
        .create();
String json = gson.toJson(new User("mlk", 27, "[email protected]", new Date()));
System.out.println(json);
//{"name":"mlk","age":27,"emailAddress":"[email protected]","birthday":"2016年09月01日"}
User user = gson.fromJson(json, User.class);
System.out.println(new SimpleDateFormat("yyyy年MM月dd日").format(user.birthday));
//2016年09月01日

3.3设置字段映射规则

上面例子已经提到使用@SerializedName注解字段名的映射规则。GsonBuilder提供了setFieldNamingPolicy接收一个枚举FieldNamingPolicy,来设置字段的映射规则。

Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY).create();
System.out.println(gson.toJson(new User("mlk", 27, "[email protected]", new Date())));
//{"name":"mlk","age":27,"emailAddress":"[email protected]","birthday":"Sep 1, 2016 3:42:50 PM"}
gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES).create();
System.out.println(gson.toJson(new User("mlk", 27, "[email protected]", new Date())));
//{"name":"mlk","age":27,"email-address":"[email protected]","birthday":"Sep 1, 2016 3:42:50 PM"}
gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
System.out.println(gson.toJson(new User("mlk", 27, "[email protected]", new Date())));
//{"name":"mlk","age":27,"email_address":"[email protected]","birthday":"Sep 1, 2016 3:43:28 PM"}
gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
System.out.println(gson.toJson(new User("mlk", 27, "[email protected]", new Date())));
//{"Name":"mlk","Age":27,"EmailAddress":"[email protected]","Birthday":"Sep 1, 2016 3:44:23 PM"}
gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES).create();
System.out.println(gson.toJson(new User("mlk", 27, "[email protected]", new Date())));
//{"Name":"mlk","Age":27,"Email Address":"[email protected]","Birthday":"Sep 1, 2016 3:44:23 PM"}
此外GsonBuilder还提供了一个setFieldNamingStrategy方法接收一个FieldNamingStrategy类型。
1
2
3
4
5
6
7
8
Gson gson = new GsonBuilder().setFieldNamingStrategy(new FieldNamingStrategy() {
    @Override
    public String translateName(Field field) {
        return field.getName().toUpperCase();
    }
}).create();
System.out.println(gson.toJson(new User("mlk", 27, "[email protected]", new Date())));
//{"NAME":"mlk","AGE":27,"EMAILADDRESS":"[email protected]","BIRTHDAY":"Sep 1, 2016 3:49:03 PM"}
FieldNamingPolicy也实现了FieldNamingStrategy接口,所以也可一个给setFieldNamingStrategy方法传入一个FieldNamingPolicy对象。

@SerializedName注解拥有最高优先级,在加有@SerializedName注解的字段上FieldNamingStrategy不生效。

3.4其他配置

GsonBuilder还提供了许多其他的配置:

1
2
3
4
5
6
7
8
9
Gson gson = new GsonBuilder()
            .serializeNulls()//允许null
            .setDateFormat("yyyy年MM月dd日")//设置时间格式
            .setPrettyPrinting()//json格式化
            .disableHtmlEscaping()//禁止转义html标签
            .disableInnerClassSerialization()//禁止序列化内部类
            //生成不可执行的Json会在json串前添加)]}'四个字符
            .generateNonExecutableJson()
            .create();

这里就不一一举例说明了。

4.字段过滤

在实际的开发中,Model中定义的一些字段可能不需要序列化,所以我们需要想办法来过滤这些字段。常用的字段过滤的方法包括: * 使用@Expose注解 * 基于版本 * 基于访问修饰符 * 基于策略

4.1@Expose

@Expose用于暴露字段。@Expose有两个属性deserialize和serialize,值为boolean值,当deserialize设置为true的时候反序列化时暴露,反之则不暴露。serialize同理。

1
2
3
4
@Expose (deserialize = false)public String name;
@Expose(serialize = false) public int age;
@Expose public String emailAddress;
public Date birthday; // deserialize = false serialize= false
使用直接new Gson()的方式,@Expose不会生效,需要调用GsonBuilder的方法来构建Gson。
1
2
3
4
5
6
7
8
9
Gson gson = new GsonBuilder()
        .excludeFieldsWithoutExposeAnnotation()
        .create();
System.out.println(gson.toJson(new User("mlk", 27, "[email protected]", new Date())));
//{"name":"mlk","emailAddress":"[email protected]"}
String json = "{\"name\":\"mlk\",\"age\":20,\"emailAddress\":\"[email protected]\"}";
User user = gson.fromJson(json, User.class);
System.out.println(user);
//User{name='null', age=20, emailAddress='[email protected]', birthday=null}

4.2版本支持

Gson提供了两个注解 @Since和@Until,@Since和@Until都接收一个Double值。我们通过GsonBuilder的setVersion()方法来设置版本号。@Since用于设置开始的版本号,@Until用于设置截止的版本号。只有当版本号大于等于@Since指定的版本号,小于@Until指定的版本号才进行序列化。

1
2
3
4
@Since(1) @Until(5) public String name;
@Since(1) @Until(3) public int age;
@Since(2) @Until(4) public String emailAddress;
public Date birthday; 
Gson gson = new GsonBuilder()
        .setVersion(4)
        .setDateFormat("yyyy年MM月dd日")
        .create();
System.out.println(gson.toJson(new User("mlk", 27, "[email protected]", new Date())));
//{"name":"mlk","emailAddress":"[email protected]"}
String json = "{\"name\":\"mlk\",\"age\":20,\"emailAddress\":\"[email protected]\"}";
User user = gson.fromJson(json, User.class);
System.out.println(user);
//User{name='mlk', age=0, emailAddress='null', birthday=null}

4.3基于修饰符进行过滤

GsonBuilder提供方法excludeFieldsWithModifiers,向该方法传入指定的修饰符,即可过滤掉相应修饰符修饰的字段。

1
2
3
4
5
6
7
8
class ModifierSample {
    final String finalField = "final";
    static String staticField = "static";
    public String publicField = "public";
    protected String protectedField = "protected";
    String defaultField = "default";
    private String privateField = "private";
}
1
2
3
4
5
6
ModifierSample modifierSample = new ModifierSample();
Gson gson = new GsonBuilder()
        .excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PRIVATE)
        .create();
System.out.println(gson.toJson(modifierSample));
//{"publicField":"public","protectedField":"protected","defaultField":"default"}

4.4自定义规则

除了上述3种方式之外,Gson还允许我们自定义规则来过滤字段。我们定义规则只需要通过实现ExclusionStrategy接口即可。GsonBuilder的两个方法addSerializationExclusionStrategy和addDeserializationExclusionStrategy用于添加序列化和反序列化的规则。

Gson gson = new GsonBuilder().addSerializationExclusionStrategy(new ExclusionStrategy() {
    @Override
    public boolean shouldSkipField(FieldAttributes fieldAttributes) {
        return "name".equals(fieldAttributes.getName());//过滤字段名为name的字段
    }

    @Override
    public boolean shouldSkipClass(Class<?> aClass) {
        return aClass == int.class;//过滤掉int类型的值
    }
}).create();

System.out.println(gson.toJson(new User("mlk", 27, "[email protected]", new Date())));
//{"emailAddress":"[email protected]","birthday":"Sep 1, 2016 3:26:11 PM"}

5.自定义序列化和反序列化

5.1 JsonSerializer与JsonDeserializer

在上面的例子中我们通过@SerializedName来改变映射规则。除此之外,我们还可以通过自定义我们自己的Serializer和Deserializer来实现相同的结果。

public class User {
    public String name;
    public int age;
    public String emailAddress;

    public User(String name, int age, String emailAddress) {
        this.name = name;
        this.age = age;
        this.emailAddress = emailAddress;
    }
}

public class UserSerializer implements JsonSerializer<User> {
    @Override
    public JsonElement serialize(User user, Type type, JsonSerializationContext jsonSerializationContext) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("name",user.name);
        jsonObject.addProperty("email_address",user.emailAddress);
        jsonObject.addProperty("age",user.age);
        return jsonObject;
    }
}
public class UserDeserializer implements JsonDeserializer<User> {
    @Override
    public User deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        //将element转换为JsonObject
        JsonObject jsonObject = jsonElement.getAsJsonObject();
        String name = jsonObject.get("name").getAsString();
        String emailAddress = jsonObject.get("email_address").getAsString();
        int age = jsonObject.get("age").getAsInt();
        User user = new User(name,age,emailAddress);
        return user;
    }
}
Gson gson = new GsonBuilder()
        .registerTypeAdapter(User.class,new UserSerializer())
        .registerTypeAdapter(User.class,new UserDeserializer())
        .create();
String json = gson.toJson(new User("mlk", 27, "[email protected]"));
System.out.println(json);
//  {"name":"mlk","email_address":"[email protected]","age":27}
System.out.println(gson.fromJson(json,User.class));

这里我们要特别说明一下JsonElement这个类。JsonElement代表一个Json元素。JsonElement有如下子类。 * JsonPrimitive:基本类型。 * JsonObject:代表一个对象。 * JsonArray:代表一个数组 * JsonNull:一个空值

1
2
3
4
5
6
7
8
9
{//JsonObject
  "primitive": 123,//JsonPrimitivte
  "null": null,//JsonNull
  "array": [//JsonArray
    "abc",
    "def",
    "ghi"
  ]
}

5.2 TypeAdapter

JsonSerializer 和JsonDeserializer解析的时候都利用到了一个中间件-JsonElement。而TypeAdapter的使用正是去掉了这个中间层,直接用流来解析数据,极大程度上提高了解析效率。

public class UserTypeAdapter extends TypeAdapter<User> {
    @Override
    public void write(JsonWriter jsonWriter, User user) throws IOException {
        jsonWriter.beginObject();
        jsonWriter.name("name").value(user.name);
        jsonWriter.name("age").value(user.age);
        jsonWriter.name("email_address").value(user.emailAddress);
        jsonWriter.endObject();
    }
    @Override
    public User read(JsonReader jsonReader) throws IOException {
        jsonReader.beginObject();
        String name=null;
        int age=0;
        String emailAddress=null;
        while (jsonReader.hasNext()){
            switch (jsonReader.nextName()){
                case "name":
                    name = jsonReader.nextString();
                    break;
                case "age":
                    age = jsonReader.nextInt();
                    break;
                case "email_address":
                    emailAddress = jsonReader.nextString();
                    break;
            }
        }
        jsonReader.endObject();
        return new User(name,age,emailAddress);
    }
}
Gson gson = new GsonBuilder()
        .registerTypeAdapter(User.class,new UserTypeAdapter())
        .create();
String json = gson.toJson(new User("mlk", 27, "[email protected]"));
System.out.println(json);
 // {"name":"mlk","email_address":"[email protected]","age":27}
System.out.println(gson.fromJson(json,User.class));
//User{name='mlk', age=27, emailAddress='[email protected]'}
@JsonAdapter注解可以作用在Model上,接收一个参数,且必须是TypeAdpater,JsonSerializer或JsonDeserializer这三个其中之一。这样就不需要调用registerTypeAdapter方法来注册。
@JsonAdapter(UserTypeAdapter.class)
public class User {
    public String name;
    public int age;
    public String emailAddress;
    public User(String name, int age, String emailAddress) {
        this.name = name;
        this.age = age;
        this.emailAddress = emailAddress;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", emailAddress='" + emailAddress + '\'' +
                '}';
    }
}
Gson gson = new Gson();
String json = gson.toJson(new User("mlk", 27, "[email protected]"));
System.out.println(json);
// {"name":"mlk","email_address":"[email protected]","age":27}
System.out.println(gson.fromJson(json, User.class));
//User{name='mlk', age=27, emailAddress='[email protected]'}

扩展阅读