Android Gson 解析Json數據過程和如何自定義解析規則(上)

友情提示:當前文章和(中)均爲過程實際沒有完美解決問題,真正解決問題的是 直接看結果

背景

Android開發與後臺API對接時,使用Gson庫做數據轉換,但是實際測試發現不夠靈活,當Gson處理正常數據時,整個流程是正確的,比如獲取用戶信息返回正確數據

{"code":200,"data":{"id":"adsd","name":"Weipru"},"msg":"success","timeStamp":"20191015102953"}

但是當你未登錄或者遇到其他錯誤信息時,後臺可能直接給你返回一個data沒有對象的字段

{"code":1000,"data":"","msg":"請登錄!","timeStamp":"20191014094718"}

 

 

這個時候如果還是用默認的GsonConverFactory解析,就會拋出一個錯誤:

Expected END_OBJECT but was String

導致整個請求回調失敗的方法,同樣,如果返回的數據是數組時也會出現類似的情,所以爲了解決這些問題,需要重寫幾個轉換器;

 

解讀源碼

我們來追溯一下錯誤的拋出原因,在創建 retrofit2請求時 會添加一個默認的Json解析器:

打開這個類可以看到兩個關鍵函數 responseBodyConverter()和requestBodyConverter(),可以看出這裏是請求Json轉換數據的出口和後臺響應Json解析數據的入口,我們要想自定義我們自己的解析方法,必須從這裏下手。這裏只看responseBodyConverter這個函數。

繼續看,這個方法最後return了一個new GsonResponseBodyConverter對象,進去看裏面只有一個函數 convert()

這個地方就是開始解析Json數據的地方,他根據你傳入的Type 調用對應的解析器進行解析,至於解析器有多少呢,這裏根據需要,大致分爲三大類:基本值類型stirng,int,float等,引用類型Object和數組List等;在源碼Gson類構造函數中可以找到

 Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy,
      final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
      boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
      boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues,
      LongSerializationPolicy longSerializationPolicy,
      List<TypeAdapterFactory> typeAdapterFactories) {
    this.constructorConstructor = new ConstructorConstructor(instanceCreators);
    this.excluder = excluder;
    this.fieldNamingStrategy = fieldNamingStrategy;
    this.serializeNulls = serializeNulls;
    this.generateNonExecutableJson = generateNonExecutableGson;
    this.htmlSafe = htmlSafe;
    this.prettyPrinting = prettyPrinting;
    this.lenient = lenient;

    List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();

    // built-in type adapters that cannot be overridden
    factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
    factories.add(ObjectTypeAdapter.FACTORY);

    // the excluder must precede all adapters that handle user-defined types
    factories.add(excluder);

    // user's type adapters
    factories.addAll(typeAdapterFactories);

    // type adapters for basic platform types
    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);
    TypeAdapter<Number> longAdapter = longAdapter(longSerializationPolicy);
    factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
    factories.add(TypeAdapters.newFactory(double.class, Double.class,
            doubleAdapter(serializeSpecialFloatingPointValues)));
    factories.add(TypeAdapters.newFactory(float.class, Float.class,
            floatAdapter(serializeSpecialFloatingPointValues)));
    factories.add(TypeAdapters.NUMBER_FACTORY);
    factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY);
    factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
    factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter)));
    factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter)));
    factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY);
    factories.add(TypeAdapters.CHARACTER_FACTORY);
    factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
    factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
    factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
    factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
    factories.add(TypeAdapters.URL_FACTORY);
    factories.add(TypeAdapters.URI_FACTORY);
    factories.add(TypeAdapters.UUID_FACTORY);
    factories.add(TypeAdapters.CURRENCY_FACTORY);
    factories.add(TypeAdapters.LOCALE_FACTORY);
    factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
    factories.add(TypeAdapters.BIT_SET_FACTORY);
    factories.add(DateTypeAdapter.FACTORY);
    factories.add(TypeAdapters.CALENDAR_FACTORY);
    factories.add(TimeTypeAdapter.FACTORY);
    factories.add(SqlDateTypeAdapter.FACTORY);
    factories.add(TypeAdapters.TIMESTAMP_FACTORY);
    factories.add(ArrayTypeAdapter.FACTORY);
    factories.add(TypeAdapters.CLASS_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));

    this.factories = Collections.unmodifiableList(factories);
  }

可以清楚發現 Gson類在初始化時爲自己註冊了各種類型的Json類型解析器TypeAdapterFactory,其中 包含兩個關鍵的解析器 CollectionTypeAdapterFactoryReflectiveTypeAdapterFactory他們都實現了接口TypeAdapterFactory,一個是list的解析,另外一個是Object的解析,也是我們要改的目標。

根據一番調試,解析數據時均從調用自身的read()方法進行解析,看源碼ReflectiveTypeAdapterFactory:

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

打開JsonReader.beginObject() :

public void beginObject() throws IOException {
    int p = peeked;
    if (p == PEEKED_NONE) {
      p = doPeek();
    }
    if (p == PEEKED_BEGIN_OBJECT) {//關鍵條件
      push(JsonScope.EMPTY_OBJECT);
      peeked = PEEKED_NONE;
    } else {
      throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() + locationString());
    }
  }

可以看到,P是代表當前讀取到的Json數據類型,比如{"name","wpr","data":{}}中,讀取到 " 時,p就代表STRING 類型,同理,{ 就是Object類型;在上面代碼中,如果請求時這樣寫

  Call<HttpMessage<UserInfo>> messageCall=retrofit2的API;//發起請求

後臺返回Json數據

{"code":1000,"data":"","msg":"請登錄!","timeStamp":"20191014094718"}

那麼當讀取到data字段時,他的值開頭時"分號,這個時候p就是STRING了,直接就走else 給你拋出

Expected BEGIN_OBJECT but was ************

最後請求回調失敗的函數,同理 其他異常也是如此***************

下一篇將解析如何處理這種問題

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章