Gson使用简析

前言

手机应用现在都已经实现联网功能,在移动网络高效的传输数据是每个应用必备的功能。目前绝大多数的服务器交换数据类型都使用Json格式,它相对于XML等其他格式占用的带宽更少,而且支持各种嵌套实现复杂的数据格式。现在来使用Google提供的Gson框架实现Json数据的解析操作。

对象转Json

使用Gson只需要简单的创建一个Gson对象,将普通的对象转换成Json格式的字符串只需要调用Gson对象的toJson方法。

首先定义一个简单的JavaBean对象User,下面只有简单的属性定义,省略了那些读取和设置方法。

public class User {
    private String id;
    private String name;
    private int age;

    private int isAdult = -1;

    public User() {
    }

    public User(String id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    ...
    // 各种getter/setter

        @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", isAdult=" + isAdult +
                '}';
    }
}

使用Gson对象将User对象转换成json字符串。

Gson gson = new Gson();
User user = new User("1000", "zhangshan", 20);
String jsonStr = gson.toJson(user);

// 结果
{"id":"1000","name":"zhangshan","userAge":20,"isAdult":-1}

结果很简单就是一个Json对象,里面包含了所有定义函数的值。

在来把List类型的User转换成一个Json字符串。

Gson gson = new Gson();
User user = new User("1000", "zhangshan", 20);
User user2 = new User("2000", "lisi", 30);
User user3 = new User("3000", "wangwu", 25);
List<User> list = new ArrayList<>();
list.add(user);
list.add(user2);
list.add(user3);
String jsonStr = gson.toJson(list);
System.out.println(jsonStr);

// 结果
[{"id":"1000","name":"zhangshan","userAge":20,"isAdult":-1},
{"id":"2000","name":"lisi","userAge":30,"isAdult":-1},
{"id":"3000","name":"wangwu","userAge":25,"isAdult":-1}]

结果也很直观就是一个数组,里面包含三个User对象。

将Json转换成对象

服务器端返回的数据都是Json格式的,但是Android使用Java这种面向对象的编程语言,将Json转换成对象更加符合开发者的开发习惯。简单的单个对象数据转换如下。

private String userJson = "{\"id\":\"1000\",\"name\":\"zhangshan\",\"age\":20}";
User user = gson.fromJson(userJson, User.class);
System.out.println(user);

// 结果
User{id='1000', name='zhangshan', age=0, isAdult=-1}

打印的结果就是生成的User对象toString返回的字符串,可以看出转换很成功。现在来看看如何实现将对象数组转换成为对象列表,因为List使用了泛型类型,而泛型在编译时会被擦出,Gson中提供了一个TypeToken的工具类,使用这个工具类生成一个匿名子类就可以获取泛型类的实际类型。

 private String userListJson = "[{\"id\":\"1000\",\"name\":\"zhangshan\",\"age\":20},{\"id\":\"2000\",\"name\":\"lisi\",\"age\":30},{\"id\":\"3000\",\"name\":\"wangwu\",\"age\":25}]";

List<User> userList = gson.fromJson(userListJson, new TypeToken<List<User>>(){}.getType());
System.out.println(userList);

// 结果
[User{id='1000', name='zhangshan', age=0, isAdult=-1}, 
User{id='2000', name='lisi', age=0, isAdult=-1}, 
User{id='3000', name='wangwu', age=0, isAdult=-1}]

结果打印除了List的toString方法返回的字符串,可以看出成功的将List对象解析出来了。

Gson Annotation使用

Gson的注解主要包含@SerializedName,为属性取别名。开发Android项目中通常使用proguard混淆工具将里面的源代码全部变成各种a,b,c等字符来提高反编译项目源代码的难度,这时候对象的属性名就被改变了,如果再从Json字符串解析就无法对应正确的属性赋值,@SerializedName注解就能很好的解决这个问题。@Export注解用于在生成Gson对象时配置了excludeFieldsWithoutExposeAnnotation时需要序列化的属性,如果属性没有配置这个注解就不会被序列化。

现在为User添加上两个注解,同时新添加一个Group对象,为它也添加上注解,实现代码如下:

public class User {
    @Expose
    private String id;

    private String name;
    @Expose
    @SerializedName("userAge")
    private int age;

    private int isAdult = -1;

    public User() {
    }

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

    // getter/setter

    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", isAdult=" + isAdult +
                '}';
    }
}

可以看到User里的name没有添加@Expose注解,而age添加了@SerializedName(“userAge”)注解,再来定义一个Group类,它的实现如下:

public class Group {
    @Expose
    private String id;
    @Expose
    private String name;
    @Expose
    private Set<User> userList;

    private int count;

    // getter/setter

    @Override
    public String toString() {
        return "Group{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", userList=" + userList +
                ", count=" + count +
                '}';
    }
}

可以看到Group的count未加expose注解,那么最终生成的json数据如下:

Gson gson = new GsonBuilder()
        .excludeFieldsWithoutExposeAnnotation()
        .create();
Group group = new Group();
group.setId("200000");
group.setName("Android开发群");
User user = new User("1000", "zhangshan", 20);
User user2 = new User("2000", "lisi", 10);
User user3 = new User("3000", "wangwu", 15);
Set<User> set = new HashSet<>();
set.add(user);
set.add(user2);
set.add(user3);
group.setUserList(set);
group.setCount(set.size());

String json = gson.toJson(group);
System.out.println(json);

// 结果
{"id":"200000","name":"Android开发群",
"userList":[{"id":"1000","userAge":20},
{"id":"3000","userAge":15},
{"id":"2000","userAge":10}]}

可以看到Group.count和User.name都未序列化,而age则变成了userAge输出。

自定义解析和反解析

前面展示的例子都是非常简单的数据对象,实际开发中见到的对象都是比较复杂的,对这些复杂对象有时侯需要自己手动来解析它们。Json字符串解析成对象其实是反序列化的过程,对象转换成Json字符串则是序列化的过程,如果两个过程都需要手动做,那么可以使用TypeAdapter转换器来实现。现在来实现手动的解析和反解析User对象。

public class UserTypeAdapter extends TypeAdapter<User> {
    @Override
    public void write(JsonWriter out, User value) throws IOException {
        // 开始对象
        out.beginObject();
        // 写入对象属性
        out.name("id").value(value.getId());
        out.name("name").value(value.getName());
        out.name("age").value(value.getAge());
        out.endObject();
    }

    @Override
    public User read(JsonReader in) throws IOException {
        // 解析json字符串
        User user = new User();
        in.beginObject();
        while (in.hasNext()) {
            switch (in.nextName()) {
                case "id":
                    user.setId(in.nextString());
                    break;
                case "name":
                    user.setName(in.nextString());
                    break;
                case "age":
                    int age = in.nextInt();
                    // 如果年龄大于18认为是成年人
                    user.setAge(age);
                    user.setAdult(age == 18 ? 0 : age > 18 ? 1 : 2);
                    break;
            }
        }
        in.endObject();
        return user;
    }
}

上面的UserTypeAdapter能在解析Json数据的时候做其他的设置操作,这在项目开发中是一个很有用的技巧。
这个转换器需要在Gson生成对象的时候注册进去,这样每次遇到User类的转换都会调用用户自定义的转换方法。

Gson gson = new GsonBuilder()
    .excludeFieldsWithoutExposeAnnotation()
    .registerTypeAdapter(User.class, new UserTypeAdapter())
    .create();

User user = gson.fromJson(userJson, User.class);
System.out.println(user);

// 结果
User{id='1000', name='zhangshan', age=20, isAdult=1}

可以看到isAdult属性被设置了。

如果用户只希望在一个方向比如序列化自定义完成,另外一个方向反序列化自动完成,或者反过来,使用JsonSerializer和JsonDeserializer两者就可以了。

public class UserSerializer implements JsonSerializer<User> {
    @Override
    public JsonElement serialize(User src, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject object = new JsonObject();
        object.addProperty("id", src.getId());
        return object;
    }
}

public class UserDeserializer implements JsonDeserializer<User> {
    @Override
    public User deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        User user = new User();
        JsonObject object = json.getAsJsonObject();
        // 将id设置到name属性上
        user.setName(object.get("id").getAsString());
        return user;
    }
}

Gson gson = new GsonBuilder()
        .excludeFieldsWithoutExposeAnnotation()
        .registerTypeAdapter(User.class, new UserDeserializer())
        .registerTypeAdapter(User.class, new UserSerializer())
        .create();

User user = gson.fromJson(userJson, User.class);
System.out.println(user);

// 结果
User{id='null', name='1000', age=0 }

可以看到成功把id设置到了name属性上,并且只有id这个属性被序列化了。

总结

Gson框架为开发这提供了功能强大的Json解析功能,除了上面的基础使用方法还有JsonReader和JsonWriter这些高级的使用方式,相对来说比较少用,也更加复杂,大部分的功能使用以上的技巧都能满足,这里就不再介绍高级的用法。

猜你喜欢

转载自blog.csdn.net/xingzhong128/article/details/79645389