日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

一個容錯的 Gson 新世界

 codingSmart 2021-10-22

闖入背景

公司項目中使用 Gson 框架對服務器傳過來的 Json 數(shù)據(jù)進行解析,而服務器后臺數(shù)據(jù)很大程度上是通過運營后臺人員配置。由于各種原因運營可能將某一字段類型配置錯誤,比如集合類型配置成字符串類型。雖然業(yè)務層會進行異常的捕獲,但是僅因為一個字段的錯誤,導致整個 Json 數(shù)據(jù)失效,因小失大,甚至可能會造成重大損失,比如直播間禮物墻,因為一個禮物的某一個字段的錯誤,導致整個禮物墻展示為空,在線上環(huán)境這個算是重大事故了。于是,一個對基本類型容錯的 Gson 改造庫的需求油然而生,對于錯誤的數(shù)據(jù)以默認值填充。

干貨地址:類型容錯的 Gson

https://github.com/1004145468/IKGson

Gson官方庫地址

Github地址

https://github.com/1004145468/IKGson

前提說明

a. 當前分析的 Gson 版本號為 2.8.1。
b. Gson 的處理過程主要分為兩個流向,一個是序列化,將 javabean 對象轉化為 json 字符串;另一個是反序列化,將 json 字符串映射成javabean 對象。
c. 這兩個流向處理前都有一個共同的操作,從傳入的 java 實例對象或者字節(jié)碼對象中獲取 TypeAdapter,對于序列化就通過 Jsonwriter 進行寫,對于反序列化就通過 JsonReader 進行讀,所以此篇只分析 Gson 讀的過程,寫處理操作流程一樣。

Gson 關鍵列的梳理

  • Gson 開發(fā)者直接使用的類,只對輸入和輸出負責。

  • TypeToken 封裝“操作類”(Gson.fromJson(json,People.class、Gson.toJson(new People)) 兩處的 People 都是操作類)的類型。

  • TypeAdapter 直接操作序列化與反序列化的過程,所以該抽象類中存在 read() 和 write 方法。

  • TypeAdapterFactory 用于生產(chǎn) TypeAdapter 的工廠類。

  • GsonReader 和 GsonWriter是 Gson 處理內容的包裝流,核心的操作有:

  • peek() 流中下一個需要處理的內容

  • nextName() 讀取 json 的 key

  • nextString() 讀取一個 String 類型的 value

  • nextInt() 讀取一個 String 類型的 value

  • nextBoolean() 讀取一個 Boolean 類型的 value

  • ...

源碼分析

從 Gson.from(json, People.class) 突入

fromJson(json,Peolple.class)的調用鏈
 public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
   Object object = fromJson(json, (Type) classOfT);
   return Primitives.wrap(classOfT).cast(object);
 }
 public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
   if (json == null) {
     return null;
   }
   StringReader reader = new StringReader(json);
   T target = (T) fromJson(reader, typeOfT);
   return target;
 }
 public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
   JsonReader jsonReader = newJsonReader(json);
   T object = (T) fromJson(jsonReader, typeOfT);
   assertFullConsumption(object, jsonReader);
   return object;
 }
 public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
   boolean isEmpty = true;
   boolean oldLenient = reader.isLenient();
   reader.setLenient(true);
   try {
     reader.peek();
     isEmpty = false;
     TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
     TypeAdapter<T> typeAdapter = getAdapter(typeToken);
     T object = typeAdapter.read(reader);
     return object;
   } ...
 }

上面是從 fromJson(String json, Class classOfT) 切入,亦或者是從fromJson(JsonElement json, Class classOfT) 也好,最終都是由 fromJson(JsonReader reader, Type typeOfT) 處理。

整個 Json 的解析過程分三步過程:

  • TypeToken 對象的獲取

  • 根據(jù) TypeToken 獲取 TypeAdapter 對象

  • 由 TypeAdapter 對象解析 json 字符串

根據(jù)以上的三步,我們逐一突破

我們先從簡單的入手,請記住我們的例子:

gson.fromJson("hello gson",String.class)

  1. TypeToken 的獲取

public static TypeToken<?> get(Type type) {
   return new TypeToken<Object>(type);
 }

沒什么好瞅的~ 看 new 吧!

TypeToken(Type type) {
   this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
   this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
   this.hashCode = this.type.hashCode();
 }

采用契約式對傳入的 type 判空處理,然后獲取 type 的(type、rawType 和 hashcode),分別看看 type 和 rawtype 的獲取流程

  1. type 的獲取(type 的華麗包裝)

public static Type canonicalize(Type type) {
   if (type instanceof Class) {
     Class<?> c = (Class<?>) type;
     return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
   } else if (type instanceof ParameterizedType) {
     ParameterizedType p = (ParameterizedType) type;
     return new ParameterizedTypeImpl(p.getOwnerType(),
         p.getRawType(), p.getActualTypeArguments());
   } else if (type instanceof GenericArrayType) {
     GenericArrayType g = (GenericArrayType) type;
     return new GenericArrayTypeImpl(g.getGenericComponentType());
   } else if (type instanceof WildcardType) {
     WildcardType w = (WildcardType) type;
     return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
   } else {
     // type is either serializable as-is or unsupported
     return type;
   }

進入條件的篩選,第一個if還是好理解,后面的是什么鬼? 不用著急,待我給施主梳理,之前 Gson.from(json, People.class)的調用鏈中有一個 fromJson(Reader json, Type typeOfT)
,用戶使用時的切入點如果是它就可能是篩選情況的其他條件,此返回的 type 相對于對傳入的java類型進行的類型的重新包裝。

  1. rawType 的獲?。╰ype 的簡單粗暴說明)

public static Class<?> getRawType(Type type) {
   if (type instanceof Class<?>) {
     // type is a normal class.
     return (Class<?>) type;
   } else if (type instanceof ParameterizedType) {
     ParameterizedType parameterizedType = (ParameterizedType) type;
     // I'm not exactly sure why getRawType() returns Type instead of Class.
     // Neal isn't either but suspects some pathological case related
     // to nested classes exists.
     Type rawType = parameterizedType.getRawType();
     checkArgument(rawType instanceof Class);
     return (Class<?>) rawType;
   } else if (type instanceof GenericArrayType) {
     Type componentType = ((GenericArrayType)type).getGenericComponentType();
     return Array.newInstance(getRawType(componentType), 0).getClass();
   } else if (type instanceof TypeVariable) {
     // we could use the variable's bounds, but that won't work if there are multiple.
     // having a raw type that's more general than necessary is okay
     return Object.class;
   } else if (type instanceof WildcardType) {
     return getRawType(((WildcardType) type).getUpperBounds()[0]);
   } else {
     String className = type == null ? "null" : type.getClass().getName();
     throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
         + "GenericArrayType, but <" + type + "> is of type " + className);
   }
 }

兩處對比的看,其實 type 和 rawtype 很相似,type 通過類來包裝說明,而 rawtype 脫去華麗的衣服。type 為GenericArrayType 的,把衣服一脫,赤身裸體的一看,擦,原來是個 array 數(shù)組,這就是 rawtype。

  1. TypeAdapter的獲取。

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);
   }

如果緩存中沒有該 Type 對應 TypeAdapter,就創(chuàng)建 TypeAdapter。前面提過 TypeAdapter是由TypeAdapterFactory創(chuàng)建的,所以有代碼:

for (TypeAdapterFactory factory : factories) {
       TypeAdapter<T> candidate = factory.create(this, type);
       if (candidate != null) {
         call.setDelegate(candidate);
         typeTokenCache.put(type, candidate);
         return candidate;
       }
     }

遍歷所有的 TypeAdapterFactory,如果該工廠能創(chuàng)建該 Type 的 TypeAdapter 就返回該TypeAdapter對象。

那么重點來了,factories 這么多的 TypeAdapterFactory 是怎么來了的?

在我們 new Gson 的時候,就往 factories 中塞入了不同類型的TypeAdapterFactory,包括 StringTypeAdapterFactory等等,代碼如下:

public Gson(xxx)
   
{
       ...
       factories.add(TypeAdapters.STRING_FACTORY);
       factories.add(TypeAdapters.STRING_FACTORY);
       factories.add(TypeAdapters.INTEGER_FACTORY);
       factories.add(TypeAdapters.BOOLEAN_FACTORY);
       factories.add(TypeAdapters.BYTE_FACTORY);
       factories.add(TypeAdapters.SHORT_FACTORY);
       ...
   }


在遍歷 factories 過程中通過 create(this,type)方法來生成TypeAdapter。

我們就以第一個 STRING_FACTORY 為例先進行說明。

public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);

接著往下看

 public static <TT> TypeAdapterFactory newFactory(
     final Class<TT> type, final TypeAdapter<TT> typeAdapter)
{
   return new TypeAdapterFactory() {
     @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
     @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
       return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null;
     }
     @Override public String toString() {
       return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]";
     }
   };
 }

STRINGFACTORY = newFactory(String.class, STRING) 的時候,STRING 就是處理 String 類型的 TypeAdapter,STRINGFACTORY中的create方法就是判斷需要處理的類型是不是 String 類型的,如果是就返回 STRING,否則返回 null,即該類型不用 STRING 來處理。

總的來說,在創(chuàng)建 Gson 的實例對象時,創(chuàng)建 TypeAdapterFactory 的集合。每種 TypeAdapterFactory 實例包含能處理的 Type 類型和 Type類型的 TypeAdapter,不能處理的 Type 類型返回的 TypeAdapter 為null,所以在遍歷 factories 過程中有:

for (TypeAdapterFactory factory : factories) {
       TypeAdapter<T> candidate = factory.create(this, type);
       if (candidate != null) {
           ...
         return candidate;
       }
     }
  1. 由TypeAdapter對象解析 json字符串

我們回到最初的代碼:

   TypeToken<T> typeToken = (TypeToken<T>)TypeToken.get(typeOfT);
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
T object = typeAdapter.read(reader);

STRING就是處理String類型的TypeAdapter,然后我們看它的read()方法。

public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
   @Override
   public String read(JsonReader in) throws IOException {
     JsonToken peek = in.peek();
     if (peek == JsonToken.NULL) {
       in.nextNull();
       return null;
     }
     /* coerce booleans to strings for backwards compatibility */
     if (peek == JsonToken.BOOLEAN) {
       return Boolean.toString(in.nextBoolean());
     }
     return in.nextString();
   }
  ...
 };

到這里位置,我們就將 gson.fromJson("hello gson",String.class) 的 String類型“hello gson”返回。
剛剛是只是牛刀小試,我們的主材料來了,看看有多豐盛...

Gson.from("{
"
name": "zhangsan",
"
age": 15,
"
grade": [
95,
98
]
}"
, Student.class)

我們重新走剛剛的流程,看看怎么處理的

Step one : 獲取TypeToken

這一步?jīng)]有什么與眾不同

Step Two: TypeAdapter的獲取。

factories中包含了很多基本類型的TypeAdapterFactory,同時也包含用戶自定義的類型Factory,看源碼:
       // type adapters for composite and user-defined types
   factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
   factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
   this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
   factories.add(jsonAdapterFactory);
   factories.add(TypeAdapters.ENUM_FACTORY);
   factories.add(new ReflectiveTypeAdapterFactory(constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));

此處我們能匹配上的是 ReflectiveTypeAdapterFactory,然后我們看它的 create()方法,關鍵的地方到了?。?!

 @Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
   Class<? super T> raw = type.getRawType();
   if (!Object.class.isAssignableFrom(raw)) {
     return null; // it's a primitive!
   }
   ObjectConstructor<T> constructor = constructorConstructor.get(type);
   return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
 }

a. constructorConstructor 獲取 Student 類的構造器
b. getBoundFields() 通過反射獲取 Student 每一個字段的的TypeAdapter,并且包裝到 Map 
中,后面會講解getBoundFields()的方法。

Step Three 通過 TypeAdapter的read()輸出對象

@Override public T read(JsonReader in) throws IOException {
     if (in.peek() == JsonToken.NULL) {
       in.nextNull();
       return null;
     }
     T instance = constructor.construct();
     try {
       in.beginObject();
       while (in.hasNext()) {
         String name = in.nextName();
         BoundField field = boundFields.get(name);
         if (field == null || !field.deserialized) {
           in.skipValue();
         } else {
           field.read(in, instance);
         }
       }
     } catch (IllegalStateException e) {
       throw new JsonSyntaxException(e);
     } catch (IllegalAccessException e) {
       throw new AssertionError(e);
     }
     in.endObject();
     return instance;
   }

到了這一步就似乎海闊天空了,通過傳入的構造器創(chuàng)建 Student 類的實例,在 JsonReader 進行處理,in.beginObject() 相當于跳過“{”,in.endObject()相當于跳過“}”,其中通過in.hasNext()判斷是否處理完成。
在 in.nextName() 讀取 json 字符串中的 key 值,然后在boundFields 根據(jù)key獲取對應的 BoundField ,最后調用BoundField.read(in,instance) 去處理細節(jié),即每個字段的映射,我們看一下內部的細節(jié):

new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
     ...
     @Override void read(JsonReader reader, Object value)
         throws IOException, IllegalAccessException
{
       Object fieldValue = typeAdapter.read(reader);
       if (fieldValue != null || !isPrimitive) {
         field.set(value, fieldValue);
       }
     }
    ...
   };

當 Filed 都處理完成后,instance 實例的每一個需要處理的字段都賦值成功,最終將這個對象 return出去。
細節(jié)說明:

a. getBoundFields()

private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
   Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
   if (raw.isInterface()) {
     return result;
   }
   Type declaredType = type.getType();
   while (raw != Object.class) {
     Field[] fields = raw.getDeclaredFields();
     for (Field field : fields) {
       boolean serialize = excludeField(field, true);
       boolean deserialize = excludeField(field, false);
       if (!serialize && !deserialize) {
         continue;
       }
       field.setAccessible(true);
       Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
       List<String> fieldNames = getFieldNames(field);
       BoundField previous = null;
       for (int i = 0, size = fieldNames.size(); i < size; ++i) {
         String name = fieldNames.get(i);
         if (i != 0) serialize = false; // only serialize the default name
         BoundField boundField = createBoundField(context, field, name,
             TypeToken.get(fieldType), serialize, deserialize);
         BoundField replaced = result.put(name, boundField);
         if (previous == null) previous = replaced;
       }
       if (previous != null) {
         throw new IllegalArgumentException(declaredType
             + " declares multiple JSON fields named " + previous.name);
       }
     }
     type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
     raw = type.getRawType();
   }
   return result;
 }

遍歷Student類的每一個字段,遍歷過程中做了兩件事情:

  • a. 該字段能否被序列化和反序列化,如果都不行就沒有必要處理該字段,主要通過注解和排除器(Excluder)進行判斷。

  • b. 對字段進行 BoundField 的包裝。

b. JsonReader.doPeek()

int doPeek() throws IOException {
   int peekStack = stack[stackSize - 1];
   if (peekStack == JsonScope.EMPTY_ARRAY) {
     stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY;
   } else if (peekStack == JsonScope.NONEMPTY_ARRAY) {
     // Look for a comma before the next element.
     int c = nextNonWhitespace(true);
     switch (c) {
     case ']':
       return peeked = PEEKED_END_ARRAY;
     case ';':
       checkLenient(); // fall-through
     case ',':
       break;
     default:
       throw syntaxError("Unterminated array");
     }
   } else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) {
     stack[stackSize - 1] = JsonScope.DANGLING_NAME;
     // Look for a comma before the next element.
     if (peekStack == JsonScope.NONEMPTY_OBJECT) {
       int c = nextNonWhitespace(true);
       switch (c) {
       case '}':
         return peeked = PEEKED_END_OBJECT;
       case ';':
         checkLenient(); // fall-through
       case ',':
         break;
       default:
         throw syntaxError("Unterminated object");
       }
     }
     int c = nextNonWhitespace(true);
     switch (c) {
     case '"':
       return peeked = PEEKED_DOUBLE_QUOTED_NAME;
     case '\'':
       checkLenient();
       return peeked = PEEKED_SINGLE_QUOTED_NAME;
     case '}':
       if (peekStack != JsonScope.NONEMPTY_OBJECT) {
         return peeked = PEEKED_END_OBJECT;
       } else {
         throw syntaxError("Expected name");
       }
     default:
       checkLenient();
       pos--; // Don't consume the first character in an unquoted string.
       if (isLiteral((char) c)) {
         return peeked = PEEKED_UNQUOTED_NAME;
       } else {
         throw syntaxError("Expected name");
       }
     }
   } else if (peekStack == JsonScope.DANGLING_NAME) {
     stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT;
     // Look for a colon before the value.
     int c = nextNonWhitespace(true);
     switch (c) {
     case ':':
       break;
     case '=':
       checkLenient();
       if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
         pos++;
       }
       break;
     default:
       throw syntaxError("Expected ':'");
     }
   } else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
     if (lenient) {
       consumeNonExecutePrefix();
     }
     stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
   } else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
     int c = nextNonWhitespace(false);
     if (c == -1) {
       return peeked = PEEKED_EOF;
     } else {
       checkLenient();
       pos--;
     }
   } else if (peekStack == JsonScope.CLOSED) {
     throw new IllegalStateException("JsonReader is closed");
   }
   int c = nextNonWhitespace(true);
   switch (c) {
   case ']':
     if (peekStack == JsonScope.EMPTY_ARRAY) {
       return peeked = PEEKED_END_ARRAY;
     }
     // fall-through to handle ",]"
   case ';':
   case ',':
     // In lenient mode, a 0-length literal in an array means 'null'.
     if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) {
       checkLenient();
       pos--;
       return peeked = PEEKED_NULL;
     } else {
       throw syntaxError("Unexpected value");
     }
   case '\'':
     checkLenient();
     return peeked = PEEKED_SINGLE_QUOTED;
   case '"':
     return peeked = PEEKED_DOUBLE_QUOTED;
   case '[':
     return peeked = PEEKED_BEGIN_ARRAY;
   case '{':
     return peeked = PEEKED_BEGIN_OBJECT;
   default:
     pos--; // Don't consume the first character in a literal value.
   }
   int result = peekKeyword();
   if (result != PEEKED_NONE) {
     return result;
   }
   result = peekNumber();
   if (result != PEEKED_NONE) {
     return result;
   }
   if (!isLiteral(buffer[pos])) {
     throw syntaxError("Expected value");
   }
   checkLenient();
   return peeked = PEEKED_UNQUOTED;
 }

該操作邏輯處理較強,主要工作分為 3 點:

  • json 的格式校驗,格式不合法拋出異常

  • 根據(jù)當前的操作,決定下一步的操作方式

  • 流中下一部分的內容類型

與之相關

Android 徹底組件化 demo 發(fā)布

使用 Dagger2 前你必須了解的一些設計原則

Android 組件化 —— 路由設計最佳實踐

    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多