Json&Gson高级进阶系列1

一.JSON概念部分

1.json概念

定义:JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。可用于数据标记,存储,传输

2.json解析示意图

json的解析一般有三种方式,即Gson,Jackson,Fastjson
Json&Gson高级进阶系列1

3.json语法格式

总体来说就是键值对

对象

Json&Gson高级进阶系列1
举例:

{
	"name":"ljh",
	"age":21
}
如果是数组,就加个中括号[]

Json&Gson高级进阶系列1
举例:array数组里面是两个对象

"array":[
	{
		"name":"ljh",
		"age":21
	},
	{
		"name":"ljh1",
		"age":22
	}
]

如果符合这些格式,就说明是格式良好的json,方便使用gson等来解析。

二.Gson

1.Gson相关的注解

@SerializedName:里面的参数作为json的key
class MyClass {
    @SerializedName("name") //a => name
    String a;
    @SerializedName(value = "name1", alternate = {"name2", "name3"})
    String b;

    String c;

    public MyClass(String a, String b, String c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

    public static void test(){
        MyClass target = new MyClass("v1", "v2", "v3");
        Gson gson = new Gson();
        String json = gson.toJson(target);
        System.out.println(json);
        /**
         * {"name":"v1","name1":"v2","c":"v3"}
         */
    }
}
@Expose:过滤掉是否需要序列化/反序列化的属性
class User0 {
    @Expose
    private String firstName;//默认参与序列化,也参与反序列化

    @Expose(serialize = false)
    private String lastName;//不参与序列化,但参与反序列化

    @Expose(serialize = false, deserialize = true)
    private String emailAddress; //不参与序列化,但参与反序列化(默认)

    @Expose(serialize = true,deserialize = false)
    private String password;//参与序列化,但不参与反序列化

    public User0(String firstName, String lastName, String emailAddress, String password) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.emailAddress = emailAddress;
        this.password = password;
    }

    @Override
    public String toString() {
        return "User0{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", emailAddress='" + emailAddress + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public static void test(){
        /**
         * 如果要让expose注解发挥作用,必须
         * new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
         * 这样来构建Gson对象
         */
        Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
        String json = gson.toJson(new User0("jinhao","liu","email","111"));
        System.out.println(json);
        /**
         * {"firstName":"jinhao","password":"111"}
         */

        User0 user0 = gson.fromJson(json,User0.class);
        System.out.println(user0);
        /**
         * User0{firstName='jinhao', lastName='null', emailAddress='null', password='null'}
         * 需要注意的是password参与序列化,所以json中可以打印password的值。但是他不参与
         * 反序列化,所以解析的时候password赋默认值null
         */
    }
}

可以看下@Expose的源码。
Json&Gson高级进阶系列1
可以发现他赋的默认值都为true

@since@until

版本控制。当使用since注解的时候,意为当版本大于等于since注解的参数的时候,被since注解修饰的属性才会参与序列化/反序列化,否则忽略

class User1 {
    private String firstName;
    private String lastName;
    @Since(1.0) private String emailAddress; //当前版本>=1.0时才会参与序列化/反序列化,否则忽略
    @Since(1.1) private String password;//当前版本>=1.0时才会参与序列化/反序列化,否则忽略

    public User1(String firstName, String lastName, String emailAddress, String password) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.emailAddress = emailAddress;
        this.password = password;
    }
}

class User2 {
    private String firstName;
    private String lastName;
    @Until(1.1) private String emailAddress;//当前版本<=1.1时参加序列化/反序列化
    @Until(1.1) private String password;//当前版本<=1.1时参加序列化/反序列化

    public User2(String firstName, String lastName, String emailAddress, String password) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.emailAddress = emailAddress;
        this.password = password;
    }
}

上面是两个类,下面是main方法

		//定义版本
		Gson gson = new GsonBuilder()
                .setVersion(1.2).create();
        User1 user1 = new User1("jinhao","liu","email","111");
        User2 user2 = new User2("jinhao","liu","email","111");
        String json1 = gson.toJson(user1);
        String json2 = gson.toJson(user2);
        System.out.println("json1:"+json1);
        System.out.println("json2:"+json2);
        /**
         * json1:{"firstName":"jinhao","lastName":"liu","emailAddress":"email","password":"111"}
         * json2:{"firstName":"jinhao","lastName":"liu"}
         */
@JsonAdapter指定自定义TypeAdapter(详细内容后面有)
@JsonAdapter(UserJsonAdapter.class)
 class User {
    public final String firstName, lastName;
    User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "User{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }
}

上面是类,下面是自定义的JsonAdapter

class UserJsonAdapter extends TypeAdapter<User> {
    @Override
    public void write(JsonWriter out, User user) throws IOException {
        out.beginObject();
        out.name("name");
        out.value(user.firstName + " " + user.lastName);
        out.endObject();
    }
    @Override
    public User read(JsonReader in) throws IOException {
        in.beginObject();
        in.nextName();
        String[] nameParts = in.nextString().split(" ");
        in.endObject();
        return new User(nameParts[0], nameParts[1]);
    }
}

下面是测试结果

User user = new User("gg","Zero");
String gsonStr = gson.toJson(user);
/**
 * gsonStr:{"name":"gg Zero"}
 * 如果不用自定义JsonAdapter,则结果为
 * gsonStr:{"firstName":"gg","lastName":"Zero"}
 */
System.out.println("gsonStr:" + gsonStr);

user = gson.fromJson(gsonStr,User.class);
System.out.println("user: " + user);
/**
 * user: User{firstName='gg', lastName='Zero'}
 */

2.Gson基本使用

import com.google.gson.Gson;

public class GsonTest {
    //普通的类,重写了toString方法
    static class GsonBean{
        public int i;
        public String str;

        @Override
        public String toString() {
            return "GsonBean{" +
                    "i=" + i +
                    ", str='" + str + '\'' +
                    '}';
        }
    }

    //基本使用
    public static void main(String ... args){
        Gson gson = new Gson();

        //转换成json
        System.out.println( gson.toJson(1));
        System.out.println( gson.toJson("zero"));
        /**
         * 1
         * "zero"
         */

        int[] values = {1,2,3};
        System.out.println( gson.toJson(values));
        /**
         * [1,2,3]
         */
        //解析json
        int i = gson.fromJson("1",int.class);
        System.out.println("i: " + i);
        /**
         * i: 1
         */
        GsonBean gsonBean = new GsonBean();
        gsonBean.i= 2;
        gsonBean.str = "str";

        String json = gson.toJson(gsonBean);
        System.out.println("json: " + json);
        /**
         * json: {"i":2,"str":"str"}
         */

        GsonBean gsonBean1 = gson.fromJson(json,GsonBean.class);
        System.out.println("gsonBean: " + gsonBean1);
        /**
         * gsonBean: GsonBean{i=2, str='str'}
         */

    }
}

3.自定义TypeAdapter

如果不指定TypeAdapter,则一般是使用使用了反射的TypeAdapter,也就是系统默认的。这种方式比较耗性能。而且有一些特殊情况无法处理。

①那么何时使用自定义TypeAdapter呢?

(1)传回的字符串或者数组为null,使用时若不加空指针判断,容易出现空指针异常。
(2)测试用的数值为0,结果用GsonFomat生成的对象默认为int类型,但可能该字段的真实类型为float,所以之后收到类型为float的数据时,就可能导致解析出错。
一般是为了解决上面两个问题才使用自定义TypeAdapter的,也就是改变解析Json的策略read方法),当然也可以改变转换成Json时的格式write方法)


注意:一个Bean类对应一个TypeAdapter或者JsonDeserializer


②先看一个正常使用的自定义TypeAdapter

Foo

static class Foo {
        private String s;
        private int i;

        public Foo() {
            this(null, 5);
        }

        public Foo(String s, int i) {
            this.s = s;
            this.i = i;
        }
    }

test2方法

public static void test2() {
//自定义TypeAdapter
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class
        , new TypeAdapter<Foo>() {

            @Override
            public void write(JsonWriter out, Foo value) throws IOException {
                if (value == null) {//进行非空判断
                    out.nullValue();
                    return;
                }
                //把Food对象制定成你自己定义的格式的字符串进行输出:不一定是json格式了,就看你怎么组织
                out.beginObject();
                out.name("s").value(value.s+"111222");
                out.name("i").value(value.i);
                out.endObject();
            }

            @Override
            public Foo read(JsonReader in) throws IOException {
                if (in.peek() == JsonToken.NULL) {//进行非空判断
                    in.nextNull();
                    return null;
                }
                //读取json串并封装成Foo对象返回之
                final Foo foo = new Foo();
                in.beginObject();
                while (in.hasNext()) {
                    switch (in.nextName()) {
                        case "s":
                            foo.s = in.nextString();
                            break;
                        case "i":
                            foo.i = in.nextInt();
                            break;
                    }
                }
                in.endObject();
                return foo;
            }
        }).create();

		Foo foo = new Foo();
		String json = gson.toJson(foo);
		System.out.println(json);
		/**
		*{"s":"null111222","i":5}
		*/
}

也可以用下面这个方法来取代

public static void test3() {
        //nullSafe()演示
        Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class
                , new TypeAdapter<Foo>() {

                    @Override
                    public void write(JsonWriter out, Foo value) throws IOException {
                        out.beginObject();
                        out.name("s").value(value.s);
                        out.name("i").value(value.i);
                        out.endObject();
                    }

                    @Override
                    public Foo read(JsonReader in) throws IOException {
                        //读取json串并封装成Foo对象返回之
                        final Foo foo = new Foo();
                        in.beginObject();
                        while (in.hasNext()) {
                            switch (in.nextName()) {
                                case "s":
                                    foo.s = in.nextString();
                                    break;
                                case "i":
                                    foo.i = in.nextInt();
                                    break;
                            }
                        }
                        in.endObject();
                        return foo;
                    }
                }.nullSafe()).create();
        Foo foo = new Foo();
        String json = gson.toJson(foo);
        System.out.println(json);
}

不同点在于上面的test2是可以自己自定义处理null,但是test3是调用了nullSafe方法来让系统帮忙处理

③再来看另外一种防止null的方式——自定义JsonDeserializer

首先看正常情况

public static void test1() {
        //TODO:
        String json = "{\n" +
                "    \"name\": \"java\",\n" +
                "    \"authors\": [\n" +
                "        {\n" +
                "            \"id\": \"1'\",\n" +
                "            \"name\": \"Joshua Bloch'\"\n" +
                "        },\n" +
                "        {\n" +
                "            \"id\": \"2'\",\n" +
                "            \"name\": \"Tom\"\n" +
                "        }\n" +
                "    ]\n" +
                "}";
        Gson gson = new Gson();
        GsonError1 gsonError1 = gson.fromJson(json, GsonError1.class);
        
        System.out.println("json:"+json);
        System.out.println(gsonError1);
    }

打印结果为

json:{
    "name": "java",
    "authors": [
        {
            "id": "1'",
            "name": "Joshua Bloch'"
        },
        {
            "id": "2'",
            "name": "Tom"
        }
    ]
}

GsonError1{name='java', authors=[AuthorsBean{id='1'', name='Joshua Bloch''}, AuthorsBean{id='2'', name='Tom'}]}

也就是说此时authors是有值的,是正常的,不为null
再看当authorsnull的时候

public static void test2() {
        //TODO:
        String json = "{\n" +
                "    \"name\": \"java\",\n" +
                "    \"authors\": \"\"\n" +
                "}";
        Gson gson = new Gson();
        GsonError1 gsonError1 = gson.fromJson(json, GsonError1.class);

        System.out.println(gsonError1);
    }

结果会报错
Json&Gson高级进阶系列1
这种情况就可以使用自定义TypeAdapter来防止报错了,比如当authorsnull的时候,我给他再赋值为null就不会报错了,或者调用nullsafe方法。打印出是

GsonError1{name='java', authors=null}

除了自定义TypeAdapter,还可以自定义JsonDeserializer

public static void test3() {
        //TODO:
        String json = "{\n" +
                "    \"name\": \"java\",\n" +
                "    \"authors\": \"\"\n" +
                "}";

        GsonBuilder gsonBuilder = new GsonBuilder();

        //注册TypeAdapter
        gsonBuilder.registerTypeAdapter(GsonError1.class, new GsonError1Deserializer());
       
        Gson gson = gsonBuilder.create();
        GsonError1 gsonError1 = gson.fromJson(json, GsonError1.class);

        System.out.println(gsonError1);
    }

GsonError1Deserializer

static class GsonError1Deserializer implements JsonDeserializer<GsonError1> {

        @Override
        public GsonError1 deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            final JsonObject jsonObject = json.getAsJsonObject();

            final JsonElement jsonTitle = jsonObject.get("name");
            final String name = jsonTitle.getAsString();

            JsonElement jsonAuthors = jsonObject.get("authors");

            GsonError1 gsonError1 = new GsonError1();

            if (jsonAuthors.isJsonArray()) {//如果数组类型,此种情况是我们需要的
                //关于context在文章最后有简单说明
                GsonError1.AuthorsBean[] authors = context.deserialize(jsonAuthors, GsonError1.AuthorsBean[].class);
                gsonError1.setAuthors(Arrays.asList(authors));
            } else {//此种情况为无效情况
                gsonError1.setAuthors(null);
            }
            gsonError1.setName(name);
            return gsonError1;
        }
    }
④面试题

Json&Gson高级进阶系列1如何解决?
自定义TypeAdapter
或自定义JsonDeserializer
或者调用nullSafe方法

上一篇:Android项目之JSON解析(3种解析技术详解)


下一篇:Gson 和 json util工具类的编写总结