Json序列化框架之Gson Api详解

Gson框架使用其实很简单,但大部分人可能只用了其中的部分功能,由于工作或功能的限制,没有太多时间去整体看看Gson到底能帮我们在数据序列化上做哪些事情,这篇文章准备根据Gson官方的API文档,同时结合项目中使用的情况做一个分享!

下面根据API 2.8.0文档里的顺序来一一解读API的内容(类名后面括号中的字符代表含义:I->interface、An->annotation、Ab->abstract、En->enum)

  1. ExclusionStrategy (I)

    根据名字的意思可以知道这是一个排除策略。该接口有两个函数:
    boolean    shouldSkipClass(Class<?> clazz)
    实现该方法可以忽略类对象,在方法中制定一个排除策略,比如类名以什么为前缀\后缀的等,然后判断当前处理的对象是否符合该规则,
    如果满足则返回true,则该类对象会当做Json对象的一部分被序列化/反序列化;
    否则返回false,则该类对象则不会作为Json解析/输出的一部分从而不会被序列化/反序列化。

    boolean    shouldSkipField(FieldAttributes f)
    改实现该方法和上面一个类似,只不过这个是针对对象属性的,是针对对象属性的排除策略,即该属性如果满足排序策略则会被序列化/反序列化,否则则被忽略。

    Sample:

    Exclude fields and objects based on a particular class type:
    
     private static class SpecificClassExclusionStrategy implements ExclusionStrategy {
       private final Class<?> excludedThisClass;
       public SpecificClassExclusionStrategy(Class<?> excludedThisClass) {
         this.excludedThisClass = excludedThisClass;
       }
    
       public boolean shouldSkipClass(Class<?> clazz) {
         return excludedThisClass.equals(clazz);
       }
    
       public boolean shouldSkipField(FieldAttributes f) {
         return excludedThisClass.equals(f.getDeclaredClass());
       }
     }
     
    Excludes fields and objects based on a particular annotation:
    
     public @interface FooAnnotation {
       // some implementation here
     }
    
     // Excludes any field (or class) that is tagged with an "@FooAnnotation"
     private static class FooAnnotationExclusionStrategy implements ExclusionStrategy {
       public boolean shouldSkipClass(Class<?> clazz) {
         return clazz.getAnnotation(FooAnnotation.class) != null;
       }
    
       public boolean shouldSkipField(FieldAttributes f) {
         return f.getAnnotation(FooAnnotation.class) != null;
       }
     }
    //使用排除策略,需要使用GsonBuilder来创建Gson对象,不能使用默认的Gson创建方式:
    ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
     Gson gson = new GsonBuilder()
         .setExclusionStrategies(excludeStrings)
         .create();
    
    //如果我们只想要序列化的时候使用该策略,反序列化是不使用,那么我们应该怎么写:
    ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
     Gson gson = new GsonBuilder()
         .addDeserializationExclusionStrategy(excludeStrings)
         .create();
    

    是不是很简单,我们只要在两个方法中定制排除策略即可,然后创建Gson对象的时候注册进去,那么在调用gson.toJson()和gson.fromJson的时候Gson框架就会使用该策略来进行排除不想要被序列化/反序列化的类对象或属性,在实际工作中不是常用的。

  2. Expose (An)

    该类型是一个注解,用来标注属性是否在序列化/反序列化时可见,但是只是在属性上添加该注解是没有作用的,必须调用用 GsonBuilder.excludeFieldsWithoutExposeAnnotation() 方法才能生效。

    Sample:

    public class User {
       @Expose private String firstName;
       @Expose(serialize = false) private String lastName;
       @Expose (serialize = false, deserialize = false) private String emailAddress;
       private String password;
     }
    

    上面一段代码是Expose在类字段上如何使用,如果不使用GsonBuilder并调用excludeFieldsWithoutExposeAnnotation()方法是没有任何作用的,即以上每个字段都会正常的被序列化/反序列化。如果调用了excludeFieldsWithoutExposeAnnotation()方法则password这个字段会被忽略,因为该字段前面没有Expose标记,在序列化过程中,被忽略的字段还有lastName和emailAddress,这是因为这两个字段中Expose的serialize属性值为false,所以会被忽略;在反序列化过程中,出了password字段还有emailAddress字段也会被忽略,因为emailAddress的Expose的deserialize属性值为false,其实如果Expose的serialize和deserialize都为false的话和不标注Expose是一样,就是忽略的意思。
    其实上面的password还有一个方式可以达到被忽略的效果,那就是使用@java.beans.Transient,可以在序列化中被忽略。

  3. FieldAttributes

    该类型是一个最终类,不可被继承,用来存储一个属性的属性或者称之为一个字段的属性。一般使用不到这个类,这个在Gson内部框架中的源代码中可以看到它的使用。在java中,一个字段的声明包括访问修饰符,类型,注解,字段标识符。那么FieldAttributes其实就是用来保存这些信息以便在使用时调用,Java程序员可能会使用反射来获取这些信息,Gson框架中把这些封装好了,直接构造FiledAttributes对象就能得到一个字段的这些信息。它的构造函数需要一个Filed对象作为参数。该类型不常用。

  4. FieldNamingPolicy (En)

    该类型是一个枚举类型,用来定义Json字段名称的几个标准策略。需要和GsonBuilder配合使用。

    包含以下枚举值:

    IDENTITY
    使用这种命名策略与Gson将确保该字段名称是不变,即默认值。

    LOWER_CASE_WITH_DASHES

    使用这种命名策略与Gson将修改java领域的名字从骆驼套管形式小写的字段名,每个字都是由一个破折号(-)分离。

    Sample:
    someFieldName ---> some-field-name
    _someFieldName ---> _some-field-name
    aStringField ---> a-string-field
    aURL ---> a-u-r-l


    LOWER_CASE_WITH_UNDERSCORES
    使用这种命名策略,Gson将修改java领域的名字从骆驼套管形式小写的字段名,每个字都是用下划线分隔(_)。

    Sample:
    someFieldName ---> some_field_name
    _someFieldName ---> _some_field_name
    aStringField ---> a_string_field
    aURL ---> a_u_r_l


    UPPER_CAMEL_CASE
    使用这种命名策略,Gson将确保首字母小写的java字段名变成大写当序列化JSON形式。

    Sample:
    someFieldName ---> SomeFieldName
    _someFieldName ---> _SomeFieldName


    UPPER_CAMEL_CASE_WITH_SPACES
    使用这种命名策略,Gson将会将每个大写开头的字符串以空格的形式分开。

    Sample:
    someFieldName ---> Some Field Name
    _someFieldName ---> _Some Field Name

  5. FieldNamingStrategy

    该类型是一个接口,用来处理序列化过程中对字段名自定义格式化的处理。FieldNamingPolicy就是对该接口的一个实现,通过GsonBuilder().setFieldNamingStrategy()来设置定制的名字格式化规则。
    注意:经过测试,如果在字段名上已经使用了注解@SerializedName设置了别名的话,FieldNamingStrategy是无效的,而且如果同时设置了FieldNamingPolicy和自定义FieldNamingStrategy的话,按照初始化gson设置的顺序最后一个值有效,因为它们都是FieldNamingStrategy的实现,所以默认最后一个设置有效。源代码中的处理的优先级SerializedName最高。

  6. Gson

    这是Gson的最主要的类,Gson通常只构建一次,然后调用toJson()和fromJson()方法,并且gson对象时线程安全的,可以跨多线程使用。Gson可以直接使用new关键字来构建一个默认的gson对象,即使用所有的默认设置,但如果需要加入自定义的行为,则需要通过GsonBuilder来创建gson对象。

  7. GsonBuilder

    在Gson类型中以及提到了GsonBuilder,用GsonBuilder可以创建自定义配置选项的Gson对象,使用方法如下:

     Gson gson = new GsonBuilder()
         .registerTypeAdapter(Id.class, new IdTypeAdapter())
         .enableComplexMapKeySerialization()
         .serializeNulls()
         .setDateFormat(DateFormat.LONG)
         .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
         .setPrettyPrinting()
         .setVersion(1.0)
         .create();

    注意:上面的设置顺序部分先后,仅当对同一选项设置重复值时,以最后一次设置为准。另外对于日期默认设置是不带时区信息的,需要手动设置setDateFormat()来修改配置。

  8. InstanceCreator (I)

    该接口用来创建反序列化实例对象,默认情况下Gson会使用无参构造函数创建一个gson对象,然后根据json的内容解析出来的值赋给创建的对应实例bean对象。但是有时候可能会有一些特殊的情况需要给bean对象添加额外的上下文信息,比如android的context对象,或者某个自定义的字段不在序列化对象中,这个值只可以在外面传递给bean对象,有人说可以等实例化好了再赋值不就行了,但有时候比如说要依赖于context对象做一些事情的话,则只能在创建bean对象时赋值,那也就是说要有一个方式能够替换gson的默认创建bean对象的方法,该接口就是这个作用。
    自定义一个InstanceCreator对象,在GsonBuilder创建gson的时候通过registerTypeAdapter()注册进去即可。
    Sample:

    public class InstanceCreatorBean {
        private String name;
        private int num;
        private int append;
    
        public InstanceCreatorBean(String name, int num, int append) {
            this.name = name;
            this.num = num;
            this.append=append;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getNum() {
            return num;
        }
    
        /*public void setNum(int num) {
            this.num = num;
        }*/
    
        public int getAppend() {
            return append;
        }
    
       /* public void setAppend(int append) {
            this.append = append;
        }*/
    }



    public class TestInstanceCreator implements InstanceCreator<InstanceCreatorBean> {
        @Override
        public InstanceCreatorBean createInstance(Type type) {
    
            return new InstanceCreatorBean("jfeoafjoe", 2090,1021);//用于附加序列化额外的私有变量值,即不属于属性的成员变量例如Android的context值
        }
    }
    

    public void instanceCreatorTest() {
            InstanceCreatorBean bean = new InstanceCreatorBean("jerrite", 1000, 2000);
            Gson gson = new GsonBuilder().setPrettyPrinting().create();
            String json = gson.toJson(bean);
            Log.d(TAG, "instanceCreatorTest:" + json);
    
            String reJson = "{" +
                    "\"name\": \"jerrite2\"," +
                    " \"num\": 1000" +
                    "}";
    
            Gson gson2 = new GsonBuilder()//.registerTypeAdapter(InstanceCreatorBean.class, new TestInstanceCreator())
                    .setPrettyPrinting()
                    .create();
            InstanceCreatorBean bean2 = gson2.fromJson(reJson, InstanceCreatorBean.class);
            if (bean2 != null) {
                Log.d(TAG, "instanceCreatorTest name:" + bean2.getName() + " num:" + bean2.getNum() + " append:" + bean2.getAppend());
            }
        }


    注意:上面示例中创建的对象传进去的参数,如果在json对象中有对应的字段信息,则会在解析json数据时被替换掉。另外,Gson对默认的bean对象的私有字段也是可以序列化和反序列化的。

  9. JsonAdapter

    该类型是一个注解,用来给类或字段注释自定义TypeAdatper对象,和通过GsonBuilder注册的效果一样,通过注解的方式可以方便开发者简化代码,以及提高可读性,并且是和bean对象时紧密相连的,具有特指性质。
    Sample:

     @JsonAdapter(UserJsonAdapter.class)
     public class User {
       public final String firstName, lastName;
       private User(String firstName, String lastName) {
         this.firstName = firstName;
         this.lastName = lastName;
       }
     }
     public class UserJsonAdapter extends TypeAdapter<User> {
       @Override public void write(JsonWriter out, User user) throws IOException {
         // implement write: combine firstName and lastName into name
         out.beginObject();
         out.name("name");
         out.value(user.firstName + " " + user.lastName);
         out.endObject();
         // implement the write method
       }
       @Override public User read(JsonReader in) throws IOException {
         // implement read: split name into firstName and lastName
         in.beginObject();
         in.nextName();
         String[] nameParts = in.nextString().split(" ");
         in.endObject();
         return new User(nameParts[0], nameParts[1]);
       }
     }

    下面这个示例用于字段:
    private static final class Gadget {
       @JsonAdapter(UserJsonAdapter2.class)
       final User user;
       Gadget(User user) {
         this.user = user;
       }
     }
    注意:可以给一个字段添加多个类型适配器,但是字段注解的优先级高于GsonBuilder注册的,而GsonBuildr注册的注解又高于类型注解设置的类型适配器。这个虽然没有测试,根据官网翻译过来的,有待验证,不过这些都不是重要的,只需要知道就行了。还有一个要注意的是注解设置的值必须是 TypeAdapter 或 TypeAdapterFactory的实现对象。.

  10. JsonArray

    这是Json的数组对象,用于手动处理数组中的每个jsonElement,当一个数组中的类型不一致时,JsonArray就可以用来手动的处理数据,JsonArray是一个有序数组,即item添加的顺序。一般情况下我们不需要用到这个对象,像List<T> 泛型 都是可以通过TypeToken直接处理的。使用JsonArray也很简单,就像遍历ArrayList一样一层层的遍历,然后通过JsonObject获取每个字段值。

  11. JsonDeserializationContext

    Gson上下文对象,在自定义JsonDeserializer的时候,在实现方法中会有这个对象,其实这个就是Gson对象,在自定义JsonDeserializer的deserialize的时候如果需要用到Gson上下文对象对其内部的某个字段类型继续反序列化,而不需要再创建一个Gson对象。
    比如:

    class A{
            private int property;
            private B b;
        }
        
        class B{
            
        }

    上面两个类,当对A对象反序列化是,如果要对property做统一修改,那么我们需要自定义反序列化,但是在Gson创建的时候已经注册了B对象的类型适配器,这时我们不要再创建一个Gson对象类解析B对象,而是直接使用传进来的上下文JsonDeserializationContext对象直接序列化即可。
    注意:传进来的JsonDeserializationContext不可以序列化当前作为Type传进来的对象,怎么理解呢?有一种情况 A对象有好多个属性,我们只要对其中一个字段做统一处理,其它的使用默认方式解析即可,但是自定义JsonDeserializer后就必须对每个字段手动的解析,那有人想就先用JsonDeserializationContext来对A做处理,然后在修改那个属性不就可以了? 这样做就会进入一个递归操作,因为Gson在序列化或反序列化时回去配置里寻找对应的自定义JsonDeserializer,如果找到了就会使用自定义的对象,这样就会形成递归,不停的自己调用自己。所以JsonDeserializationContext除了当前解析的类型对象不能重复利用,该类型对象的字段类型则都可以复用JsonDeserializationContext对象。

  12. JsonDeserializer (I)

    该接口就是当要自定义反序列化器的时用到的j接口,在JsonDeserializationContext已经介绍过了,这里不多做介绍,当自定义一个反序列化器的时候,只要实现对应的方法,然后在GsonBuilder中注册即可。

  13. JsonElement (Ab)

    这是一个抽象类,是 JsonObject, a JsonArray, a JsonPrimitive or a JsonNull的父类。其中封装了好多方法,通过对应的方法可以将该对象转换为具体的基本类型,或它的子类型。

  14. JsonIOException

    这个就不用说了,gson框架的异常类,当序列化或反序列化过程中IO异常时就可以用该类来获取详细的异常信息。

  15. JsonNull

    和NULL对象对应,在Json序列化或反序列化过程中,当遇到空对象时,就可以用该对象来表示。

  16. JsonObject

    Json对象,对象包含key-value的键值对值,key是String型的值,value是其它的JsonElement对象。JsonObject对象中的成员顺序是按照被添加的顺序保存在对象中的。该对象封装了好多方法,包括添加字段,转换对应的JsonElement对象等。查看API一目了然,这里不做太多解释。JsonObject其实就是一个具体的Json对象,可以使最外层的对象也可以是某个字段对象。

  17. JsonParseException

    略过。

  18. JsonParser

    将Json解析成JsonElements的解析树,然后就可以根据JsonElement一层一层的获取每个JsonElement的值了。说白了就是讲Json字面值的格式 转换成JsonElemnt对象。在手动解析Json对象时,我们需要先将Json字符串或Json流转换成JsonElement对象,然后在转换成JsonObject或JsonArray等具体的类型,然后再一层一层的解析,最终得到每个对象每个字段的值。

  19. JsonPrimitive

    该类型其实是对基础类型的封装,可以转换为int、string、long、float等或者java原始封装的类型。其实就是Gson定义的一个基本类型转换器或者类型封装器,用来将Java的基本类型描述成Json的类型。

  20. JsonReader

    这个类算是Gson解析Json对象的关键类了,fromJson()其实最终解析的对象就是这个对象,包括JsonParser它的内部实现也是基于JsonReader。
    该版本的JsonReader对象是基于RFC7159的标准读取解析Json的令牌的即标识符(是一个大括号的对象 还是方括号的数组),JsonReader会将Json对象读取到一个Stream中,该Stream中包含了 (strings, numbers, booleans, and nulls)的字面值,同时包含了json对象和json数组的分隔符。JsonReader是按照深度优先的顺序读取令牌的。
    JsonReader是如何解析JsonObject的:
    首先调用beginObject()方法标识开始解析一个单个对象,并且消耗(读取)一个大括号,然后循环的根据字段名获取对应的值,当hasNext()返回false时,循环结束,当调用endObject()方法代表读取object结束。
    JsonReader是如何解析JsonArray的:
    首先调用beginArray()方法标识开始解析一个数组对象,然后循环分别读里面的每个object,当hasNext()返回false时,循环结束,当调用endObject()方法代表读取array结束。
    Sample:
    Json字面值:

    [
       {
         "id": 912345678901,
         "text": "How do I read a JSON stream in Java?",
         "geo": null,
         "user": {
           "name": "json_newb",
           "followers_count": 41
          }
       },
       {
         "id": 912345678902,
         "text": "@json_newb just use JsonReader!",
         "geo": [50.454722, -104.606667],
         "user": {
           "name": "jesse",
           "followers_count": 2
         }
       }
     ]

    解析步骤:
    public List<Message> readJsonStream(InputStream in) throws IOException {
         JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
         try {
           return readMessagesArray(reader);
         } finally {
           reader.close();
         }
       }
    
       public List<Message> readMessagesArray(JsonReader reader) throws IOException {
         List<Message> messages = new ArrayList<Message>();
    
         reader.beginArray();
         while (reader.hasNext()) {
           messages.add(readMessage(reader));
         }
         reader.endArray();
         return messages;
       }
    
       public Message readMessage(JsonReader reader) throws IOException {
         long id = -1;
         String text = null;
         User user = null;
         List<Double> geo = null;
    
         reader.beginObject();
         while (reader.hasNext()) {
           String name = reader.nextName();
           if (name.equals("id")) {
             id = reader.nextLong();
           } else if (name.equals("text")) {
             text = reader.nextString();
           } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
             geo = readDoublesArray(reader);
           } else if (name.equals("user")) {
             user = readUser(reader);
           } else {
             reader.skipValue();
           }
         }
         reader.endObject();
         return new Message(id, text, user, geo);
       }
    
       public List<Double> readDoublesArray(JsonReader reader) throws IOException {
         List<Double> doubles = new ArrayList<Double>();
    
         reader.beginArray();
         while (reader.hasNext()) {
           doubles.add(reader.nextDouble());
         }
         reader.endArray();
         return doubles;
       }
    
       public User readUser(JsonReader reader) throws IOException {
         String username = null;
         int followersCount = -1;
    
         reader.beginObject();
         while (reader.hasNext()) {
           String name = reader.nextName();
           if (name.equals("name")) {
             username = reader.nextString();
           } else if (name.equals("followers_count")) {
             followersCount = reader.nextInt();
           } else {
             reader.skipValue();
           }
         }
         reader.endObject();
         return new User(username, followersCount);
       }
    注意:在处理数值型Json对象时,JsonReader可以将numeric的值作为String值读取,也可以将String的值作为numeric的值读取。比如JsonArray[1,"1"],里面包含了一个整型值1和字符串整数值“1”,为了防止数值转换精确度丢失,可以使用nextInt()和nextString()来读取。对于特别大的数值使用字符串保存比如double是javascript中的唯一数值类型,当一个数字特别大操作了double类型可表示的值时,那么数值的精度就会丢失,从而使得计算不准确。
    对于Json格式的完整性,有时候web服务器可能遭到黑客攻击,在Json数据中添加<script>这样的HTML标签,此时将Json的前缀改为")]}'\n" ,从而使得<script>不可执行,从而防止了js攻击。但是Json这样修改后结构就不是正确的Json格式,从而导致解析出错了,不过JsonReader能够兼容这种不可执行的前缀,只要设置setLenient(true)即可。另外一个JsonReader对象时非线程安全的,每个JsonReader对象可能用于读取单个json stream

  21. JsonSerializationContext

    参照JsonDeserializationContext的使用。

  22. JsonSerializer

    参照JsonDeserializer的使用。

  23. JsonStreamParser

    改parser可以同时解析多个JsonElement,之前的JsonParser只能解析一个JsonElement对象,即Json字面值是以一个大括号开始的。而这个Parser可以同时解析多个JsonElement,可以使JsonArray、JsonObject等JsonElement的子类的混合形式,比如:  "['json 1 first'] {'json 1 second':10} 'json 1 third'";这个就是多对象混合的例子,由于服务器端会根据某些特殊的业务需求,如果按照正常的Json规范去解析 这可能需要定义一个超级类去包含多个对象,假如定了三个实体类对象 A 、B和C,如果要一次把这三个对象返回给调用者,那么一般做法会再定义一个类 然后把这三个类作为三个字段然后在封装成Json传输,根据面向对象思想,这个额外定义的类是没有任何意义的,只是为了提供这么一个接口而定义,那么这时用JsonStreamParser就可以不需要额外定义多余的类对象,只要将三个对象值按照某个顺序序列化为一个Json流数据,然后调用者按照这个顺序去分别解析即可。
    JsonStreamParser根据官方的解释是线程安全的,但是是有条件的,具体什么意思没理解,不过根据demo的测试总结如下:如果跨线程去处理Json数据,JsonStreamParser解析之前需要做同步处理,否则会报错,下面是两次实验得出的错误信息:
      Error1:java.lang.IllegalStateException: Expected a string but was END_ARRAY
      Error2:com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Unterminated array
    明显这两个信息不一样,为什么会这样?其实当parser.next()被调用的时候,parser会去解析Json内容,之所以叫StreamParser,它的数据其实也是基于StreamReader读取的,那么next()调用一次就会从缓存中取出一部分数据,如果两个线程同时操作next()方法,那么就是两个线程同时读取一个缓存中的数据,导致最后格式不正确,所以要在读取之前加同步锁!
    Sample:

    private class StreamParserRunable implements Runnable {
            private JsonStreamParser parser;
    
            public StreamParserRunable(JsonStreamParser parser) {
                this.parser = parser;
    
            }
    
            @Override
            public void run() {
                JsonElement element;
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //synchronized (parser) {
                        if(!parser.hasNext()){
                            break;
                        }
                        element = parser.next();
                        if (element.isJsonArray()) {
                            Log.d(TAG, Thread.currentThread().getName() + "---element is jsonArray." + element.getAsString());
                        } else if (element.isJsonObject()) {
                            Log.d(TAG, Thread.currentThread().getName() + "---element is jsonObject." + element.getAsJsonObject().get("json 1 second"));
                        } else if (element.isJsonNull()) {
                            Log.d(TAG, Thread.currentThread().getName() + "---element is jsonNull.");
                        } else if (element.isJsonPrimitive()) {
                            Log.d(TAG, Thread.currentThread().getName() + "---element is jsonPrimitive." + element.getAsString());
                        }
                    //}
                }
            }
        }
    
        private class StreamParserRunable2 implements Runnable {
            private JsonStreamParser parser;
    
            public StreamParserRunable2(JsonStreamParser parser) {
                this.parser = parser;
    
            }
    
            @Override
            public void run() {
                JsonElement element;
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //synchronized (parser) {
                        if(!parser.hasNext()){
                            break;
                        }
    
                        element = parser.next();
                        if (element.isJsonArray()) {
                            Log.d(TAG, Thread.currentThread().getName() + "---element is jsonArray." + element.getAsString());
                        } else if (element.isJsonObject()) {
                            Log.d(TAG, Thread.currentThread().getName() + "---element is jsonObject." + element.getAsJsonObject().get("json 1 second"));
                        } else if (element.isJsonNull()) {
                            Log.d(TAG, Thread.currentThread().getName() + "---element is jsonNull.");
                        } else if (element.isJsonPrimitive()) {
                            Log.d(TAG, Thread.currentThread().getName() + "---element is jsonPrimitive." + element.getAsString());
                        }
                    }
                //}
            }
        }

    public void testMultipleThreads() {
            String json1 = "['json 1 first'] {'json 1 second':10} 'json 1 third'";
            String json3 = "['json 2 first'] {'json 2 second':10} 'json 2 third'";
            JsonStreamParser streamParser = new JsonStreamParser(json1);
            cachedThreadPool.execute(new StreamParserRunable(streamParser));
            cachedThreadPool.execute(new StreamParserRunable2(streamParser));
        }
    写个demo测试便可知其中原理,无序分析源代码。至于conditionally thread-safe是什么意思,可以看看Thread_safety里面描述的比较详细。


  24. JsonSyntaxException

    Json语法错误异常,略过。

  25. JsonToken

    该枚举类型是对Json所有标识符的描述,比如JsonToken BEGIN_ARRAY和JsonToken END_ARRAY代表JsonArray对象的开始和结束标记,目的就是为了在整个框架中对标识符的统一描述,比使用常量值“[ ]”语义更明确。在调用某些方法写入数据或解析某个JsonElement时判断标识符是什么时会用到。

  26. JsonWriter

    和JsonReader对应,通过JsonWriter可以构造一个Json对象。
    Sample:
    [
    {
    "id": 912345678901,
    "text": "How do I stream JSON in Java?",
    "geo": null,
    "user": {
    "name": "json_newb",
    "followers_count": 41
    }
    },
    {
    "id": 912345678902,
    "text": "@json_newb just use JsonWriter!",
    "geo": [50.454722, -104.606667],
    "user": {
    "name": "jesse",
    "followers_count": 2
    }
    }
    ]

     public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
         JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
         writer.setIndent("    ");
         writeMessagesArray(writer, messages);
         writer.close();
       }
    
       public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
         writer.beginArray();
         for (Message message : messages) {
           writeMessage(writer, message);
         }
         writer.endArray();
       }
    
       public void writeMessage(JsonWriter writer, Message message) throws IOException {
         writer.beginObject();
         writer.name("id").value(message.getId());
         writer.name("text").value(message.getText());
         if (message.getGeo() != null) {
           writer.name("geo");
           writeDoublesArray(writer, message.getGeo());
         } else {
           writer.name("geo").nullValue();
         }
         writer.name("user");
         writeUser(writer, message.getUser());
         writer.endObject();
       }
    
       public void writeUser(JsonWriter writer, User user) throws IOException {
         writer.beginObject();
         writer.name("name").value(user.getName());
         writer.name("followers_count").value(user.getFollowersCount());
         writer.endObject();
       }
    
       public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
         writer.beginArray();
         for (Double value : doubles) {
           writer.value(value);
         }
         writer.endArray();
       }

    注意:JsonWriter是线程安全的,需要再单独的线程用使用。

  27. LongSerializationPolicy

    该枚举类型是用来定义long型值的默认格式化方式,在上面也提到了,在某些平台或语言中,不支持long型在转换过程中不够精确,所以可以保存为String类型。该枚举有两个值:
    DEFAULT
    默认输出long型对象;
    STRING
    输出为String类型对象。

  28. MalformedJsonException

    当解析一个错误格式的Json对象时会报此异常。但是有些语法错误可以通过JsonReader.setLenient(boolean).来忽略。在上面的防止Html中javascript攻击内容中已经介绍过了。

  29. SerializedName

    这是一个注解,用来标识字段序列化和反序列化时的字段名字。在名字策略中已经介绍过了。

  30. Since

    这个注解用来标记某个字段在某个版本中是否被序列化或反序列化。 Since表示在Since的value值的版本以及以后的版本中该字段存在,也就表示可以被序列化和反序列化。Since的value值的比较对象是在GsonBuilder.setVersion(version)设置的值。它的作用就是在发布版本时,有可能某些字段是在某个版本后才有的,因此老版本中需要对其忽略过滤。
    Sample:

    public class User {
       private String firstName;
       private String lastName;
       @Since(1.0) private String emailAddress;
       @Since(1.0) private String password;
       @Since(1.1) private Address address;
     }

  31. TypeAdapter

    这是Gson框架中一个核心类之一。用来自定义json类型适配器,通过GsonBuilder注册到gson中,以后通过toJson()/fromJson()时Gson就会去配置里去找到对应应的类型适配器,如果存在则选用,否则使用默认的类型适配器。上面既然有了自定义JsonSerializer和JsonDeserializer用来处理特殊的解析需求,TypeAdapter又有什么优势呢?其实JsonSerializer和JsonDeserializer最终也是被封装成了 TreeTypeAdapter对象,然后放到了adapter集合里,官方在最下面写了一句“ New applications should prefer TypeAdapter, whose streaming API is more efficient than this interface's tree API.”意思就是自定义解析方式选用TypeAdapter比JsonSerializer和JsonDeserializer更高效,具体可以看源代码里是怎么实现的。
    Sample:

    public class PointAdapter extends TypeAdapter<Point> {
         public Point read(JsonReader reader) throws IOException {
           if (reader.peek() == JsonToken.NULL) {
             reader.nextNull();
             return null;
           }
           String xy = reader.nextString();
           String[] parts = xy.split(",");
           int x = Integer.parseInt(parts[0]);
           int y = Integer.parseInt(parts[1]);
           return new Point(x, y);
         }
         public void write(JsonWriter writer, Point value) throws IOException {
           if (value == null) {
             writer.nullValue();
             return;
           }
           String xy = value.getX() + "," + value.getY();
           writer.value(xy);
         }
       }
      GsonBuilder builder = new GsonBuilder();
       builder.registerTypeAdapter(Point.class, new PointAdapter());
       // if PointAdapter didn't check for nulls in its read/write methods, you should instead use
       // builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe());
       ...
       Gson gson = builder.create();
     

    如果使用默认的类型适配器,那么point的json字面值应该是: {"x":5,"y":8}, 上面的adapter则可以解析一个"5,8"这样字符串为point对象,活着序列化为"5,8"这样的字符串。这样就能更加灵活的处理接口提供的数据。这有点类似于重载运算符一样。

  32. TypeAdapterFactory

    上面有个TypeAdapter,那这个TypeAdapterFactory有时干嘛用的?TypeAdapter只针对一个具体的类型定制,那如果有几个类似的结构是不是要每个都定义一个TypeAdapter呢?当然不是,定义TypeAdapterFactory则可以解决这一问题,TypeAdapterFactory用来处理一类类型的自定义适配问题,现在都是面向对象思想了,所以应该不难理解。
    Sample:

     public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory {
         public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
           Class<T> rawType = (Class<T>) type.getRawType();
           if (!rawType.isEnum()) {
             return null;
           }
    
           final Map<String, T> lowercaseToConstant = new HashMap<String, T>();
           for (T constant : rawType.getEnumConstants()) {
             lowercaseToConstant.put(toLowercase(constant), constant);
           }
    
           return new TypeAdapter<T>() {
             public void write(JsonWriter out, T value) throws IOException {
               if (value == null) {
                 out.nullValue();
               } else {
                 out.value(toLowercase(value));
               }
             }
    
             public T read(JsonReader reader) throws IOException {
               if (reader.peek() == JsonToken.NULL) {
                 reader.nextNull();
                 return null;
               } else {
                 return lowercaseToConstant.get(reader.nextString());
               }
             }
           };
         }
    
         private String toLowercase(Object o) {
           return o.toString().toLowerCase(Locale.US);
         }
       }
    GsonBuilder builder = new GsonBuilder();
      builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());
      ...
      Gson gson = builder.create();
     public class MultisetTypeAdapterFactory implements TypeAdapterFactory {
         public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
           Type type = typeToken.getType();
           if (typeToken.getRawType() != Multiset.class
               || !(type instanceof ParameterizedType)) {
             return null;
           }
    
           Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
           TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
           return (TypeAdapter<T>) newMultisetAdapter(elementAdapter);
         }
    
         private <E> TypeAdapter<Multiset<E>> newMultisetAdapter(
             final TypeAdapter<E> elementAdapter) {
           return new TypeAdapter<Multiset<E>>() {
             public void write(JsonWriter out, Multiset<E> value) throws IOException {
               if (value == null) {
                 out.nullValue();
                 return;
               }
    
               out.beginArray();
               for (Multiset.Entry<E> entry : value.entrySet()) {
                 out.value(entry.getCount());
                 elementAdapter.write(out, entry.getElement());
               }
               out.endArray();
             }
    
             public Multiset<E> read(JsonReader in) throws IOException {
               if (in.peek() == JsonToken.NULL) {
                 in.nextNull();
                 return null;
               }
    
               Multiset<E> result = LinkedHashMultiset.create();
               in.beginArray();
               while (in.hasNext()) {
                 int count = in.nextInt();
                 E element = elementAdapter.read(in);
                 result.add(element, count);
               }
               in.endArray();
               return result;
             }
           };
         }
       }

    上面两个例子是官方给出的,基本看完例子也就能明白这个TypeAdapterFactory怎么用了。有人会问了,这个TypeAdapterFactory只在create里面对类型进行区分,注册的时候并没有与具体的类型关联,那Gson是如何适配的呢?如果非要纠结具体的实现那就看源代码吧,下面把关键的源代码贴出来:
     public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
        TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
        if (cached != null) {
          return (TypeAdapter<T>) cached;
        }
    
        Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
        boolean requiresThreadLocalCleanup = false;
        if (threadCalls == null) {
          threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
          calls.set(threadCalls);
          requiresThreadLocalCleanup = true;
        }
    
        // the key and value type parameters always agree
        FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
        if (ongoingCall != null) {
          return ongoingCall;
        }
    
        try {
          FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
          threadCalls.put(type, call);
    
          for (TypeAdapterFactory factory : factories) {
            TypeAdapter<T> candidate = factory.create(this, type);
            if (candidate != null) {
              call.setDelegate(candidate);
              typeTokenCache.put(type, candidate);
              return candidate;
            }
          }
          throw new IllegalArgumentException("GSON cannot handle " + type);
        } finally {
          threadCalls.remove(type);
    
          if (requiresThreadLocalCleanup) {
            calls.remove();
          }
        }
      }
    值得注意的一点,注册TypeAdapter和前面提到的注册排除策略不同,排除策略对同一类型注册多个策略时以最后一个为准,其实内部是使用map保存的,同一个key不能有重复的值,而TypeAdapter则相反,是最先注册的会被使用。所以上面的代码会先到缓存中获取如果没有继续到当前threadLocal对象中获取,如果还没有则遍历所有的factories,所以会在这个方法里对注册的所有typeadapter进行筛选,最先注册的优先。
    再看一段代码即可明白所有的TypeAdapter以及JsonSerializer和 JsonDeserializer最后都是封装成了TypeAdapterFactory对象:
    public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
        $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
            || typeAdapter instanceof JsonDeserializer<?>
            || typeAdapter instanceof InstanceCreator<?>
            || typeAdapter instanceof TypeAdapter<?>);
        if (typeAdapter instanceof InstanceCreator<?>) {
          instanceCreators.put(type, (InstanceCreator) typeAdapter);
        }
        if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
          TypeToken<?> typeToken = TypeToken.get(type);
          factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
        }
        if (typeAdapter instanceof TypeAdapter<?>) {
          factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
        }
        return this;
      }
    

    这个可能比较复杂,需要看源代码了解原理,但正常使用不需要了解和么多,只要实现对应的方法就行了。

  33. TypeToken

    这个是Gson对java Class的类型元数据的封装,比如类型名,类型完整名,泛型类型等等。具体查看api即可。

  34. Until

    这个注解和Since的作用类似,只不过是Since表示自某个版本后该字段可以被识别,而Until则表示被标注的字段在某个版本之前可以别识别。因为在版本更新过程中有可能对某些代码重构,有可能有些字段被弃用了,所以在版本切换过程中这两个注解是很有用的。
    Smaple:

    public class User {
       private String firstName;
       private String lastName;
       @Until(1.1) private String emailAddress;
       @Until(1.1) private String password;
     }
  35. 部分原理解析

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值