Gson解析原理

316 阅读6分钟

问:

  1. 为什么解析的时候int会自动转成double
  2. float数据为什么会丢失

解析流程

graph TD
开始解析fromJson --> 获取解析的类型TypeToken --> 通过类型获取对应的Adapter --> 通过read方法获取对应的字段 --> 反射的方式获取字段的值 --> 每个字段都会对应一个Adapter --> 调用JsonReader的next系列方法截取对应的值-->调用系统方法直接将截取的值转化成对应的类型-->返回对应的字段值每个字段的解析类似
//下面一段代码解析流程是怎样的呢
var gsonStr = "{"code":"0","message":"","data":{"format":"#########0.###","format2":3.111110498093283782,"format3":3.045124912412,"format4":3.99992753112},"success":true}"
var gson = Gson().newBuilder().also {
    //it.serializeSpecialFloatingPointValues()
}.create()
val model: Model =  gson.fromJson<Model>(gsonStr,
    Model::class.java
)

//数据类
data class Model(var code: String, var result: String?,var message:String?,var msg:String?, var data: Data)
data class Data(var format: String, var accuracy: Float?,var comma:Float?,var zero:Float?)

fromJson方法:通过传入string,初始化对应TypeToken,然后在找到对应Adapter,通过Adapter进行解析

//Gson.java文件
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,只是string的包装类
  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 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;
    //获取当前类型对应的token,说白了只是简单包装一下
    TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
    //获取解析的Adapter
    TypeAdapter<T> typeAdapter = getAdapter(typeToken);
    //解析成对应得数据类型
    T object = typeAdapter.read(reader);
    return object;
  } catch (EOFException e) {
    /*
     * For compatibility with JSON 1.5 and earlier, we return null for empty
     * documents instead of throwing.
     */
    if (isEmpty) {
      return null;
    }
    throw new JsonSyntaxException(e);
  } catch (IllegalStateException e) {
    throw new JsonSyntaxException(e);
  } catch (IOException e) {
    // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException
    throw new JsonSyntaxException(e);
  } catch (AssertionError e) {
    AssertionError error = new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage());
    error.initCause(e);
    throw error;
  } finally {
    reader.setLenient(oldLenient);
  }
}

TypeToken.get(typeOfT)方法:获取对应的TypeToken,通过对应的解析类型方便后续获得想要的Adapter

这里主要理解TypeToken这个类:
public class TypeToken<T> {
    final Class<? super T> rawType;
    final Type type;
    final int hashCode;
  
    TypeToken(Type type) {
      //获取对应的Type,实际上就是Class
      this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
      //真实的Class带泛型类型的
      this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
      //注意这里,重新定义了hashcode,用来在Map里面比对两个TypeToken是不是同一个对象,外界一般传递的是
      //class,所以只需要是一样的class,那hashcode就是一样得,那最后在Gson的typeTokenCache或者calls这个Map结构存的
      //key是同一个,也就保障了后边取值的时候取得是同一个
      this.hashCode = this.type.hashCode();
    }
    //这里比较的是两个对象是否相等
    @Override public final boolean equals(Object o) {
      return o instanceof TypeToken<?>
          && $Gson$Types.equals(type, ((TypeToken<?>) o).type);
    }
  
}

/*******$Gson$Types.equals方法******/
public static boolean equals(Type a, Type b) {
  if (a == b) {
    // also handles (a == null && b == null)
    return true;

  } else if (a instanceof Class) {
    // Class already specifies equals().
    //这里调用的是Object的方法,这里应该明白判定两个TypeToken对象是否相等逻辑了吧
    //就是判定其T泛型是否相等,也就是你传入的解析class类型是否一致,如果一致就是一样的Adapter
    return a.equals(b);

  } 
  /*******省略其他代码******/
    
}

/*******截取代码*******/
//传过来的Type一般是解析目标对象的class类型
public static Type canonicalize(Type type) {
  if (type instanceof Class) {
    Class<?> c = (Class<?>) type;
    return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;

  }
   /*******省略其他代码******/
}
/*******截取代码*******/
//传过来的Type一般是解析目标对象的class类型
public static Class<?> getRawType(Type type) {
  if (type instanceof Class<?>) {
    // type is a normal class.
    return (Class<?>) type;

  } 
   /*******省略其他代码******/
}

getAdapter方法:从factories中获取对应得Adapter,并缓存在当前线程中

public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
  //缓存中获取adapter
  TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
  if (cached != null) {
    return (TypeAdapter<T>) cached;
  }
   //当前线程中得adapter集合
  Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
  boolean requiresThreadLocalCleanup = false;
  //当前线程没有Adapter集合就创建一个
  if (threadCalls == null) {
    threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
    calls.set(threadCalls);
    requiresThreadLocalCleanup = true;
  }

  // the key and value type parameters always agree
  //缓存中获取Adapter
  FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
  if (ongoingCall != null) {
    return ongoingCall;
  }

  try {
  //新建Adapter代理工厂类,只是代理了Adapter得处理逻辑
    FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
    threadCalls.put(type, call);

    for (TypeAdapterFactory factory : factories) {
        //此方法很关键,factories是Gson初始化得时候构建
      TypeAdapter<T> candidate = factory.create(this, type);
      if (candidate != null) {
      //设置具体得代理类,方便方法回调
        call.setDelegate(candidate);
        typeTokenCache.put(type, candidate);
        //找到对应得Adapter并返回
        return candidate;
      }
    }
    throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
  } finally {
    threadCalls.remove(type);

    if (requiresThreadLocalCleanup) {
      calls.remove();
    }
  }
}

factories:添加默认得解析工厂,比如Object、Long、Double等类型,根据需要解析的数据类型,返回对应的Adapter

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

// users' type adapters
factories.addAll(factoriesToBeAdded);

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

注:如果在解析得时候有传入具体得类型,走的是反射方式获取该类型的字段并调用对应的Adapter进行字段解析;

ReflectiveTypeAdapterFactory创建对应的反射Adapter

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

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;
      }
      accessor.makeAccessible(field);
      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 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;
}

//
private ReflectiveTypeAdapterFactory.BoundField createBoundField(
    final Gson context, final Field field, final String name,
    final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
  final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
  // special casing primitives here saves ~5% on Android...
  JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
  TypeAdapter<?> mapped = null;
  if (annotation != null) {
    mapped = jsonAdapterFactory.getTypeAdapter(
        constructorConstructor, context, fieldType, annotation);
  }
  final boolean jsonAdapterPresent = mapped != null;
  if (mapped == null) mapped = context.getAdapter(fieldType);

  final TypeAdapter<?> typeAdapter = mapped;
  return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
    @SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
    @Override void write(JsonWriter writer, Object value)
        throws IOException, IllegalAccessException {
      Object fieldValue = field.get(value);
      TypeAdapter t = jsonAdapterPresent ? typeAdapter
          : new TypeAdapterRuntimeTypeWrapper(context, typeAdapter, fieldType.getType());
      t.write(writer, fieldValue);
    }
    //主要是read返回在Adapter中回调
    @Override void read(JsonReader reader, Object value)
        throws IOException, IllegalAccessException {
      Object fieldValue = typeAdapter.read(reader);
      if (fieldValue != null || !isPrimitive) {
      //此处是通过Adapter读取到值之后进行赋值
        field.set(value, fieldValue);
      }
    }
    @Override public boolean writeField(Object value) throws IOException, IllegalAccessException {
      if (!serialized) return false;
      Object fieldValue = field.get(value);
      return fieldValue != value; // avoid recursion for example for Throwable.cause
    }
  };
}

public static final class Adapter<T> extends TypeAdapter<T> {
  private final ObjectConstructor<T> constructor;
  private final Map<String, BoundField> boundFields;

  Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
    this.constructor = constructor;
    this.boundFields = boundFields;
  }

  @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();
    //通过字段名获取对应的字段,并赋值给对应的实例instance
        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;
  }
}

next方法获取对应的值

public double nextDouble() throws IOException {
  int p = peeked;
  if (p == PEEKED_NONE) {
    p = doPeek();
  }

  if (p == PEEKED_LONG) {
    peeked = PEEKED_NONE;
    pathIndices[stackSize - 1]++;
    return (double) peekedLong;
  }

  if (p == PEEKED_NUMBER) {
  //直接截取对应的长度,其中peekedNumberLength的值是在doPeek()方法中确认的
    peekedString = new String(buffer, pos, peekedNumberLength);
    pos += peekedNumberLength;
  } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
    peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? ''' : '"');
  } else if (p == PEEKED_UNQUOTED) {
    peekedString = nextUnquotedValue();
  } else if (p != PEEKED_BUFFERED) {
    throw new IllegalStateException("Expected a double but was " + peek() + locationString());
  }

  peeked = PEEKED_BUFFERED;
  //解析成对应的数据类型
  double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
  if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
    throw new MalformedJsonException(
        "JSON forbids NaN and infinities: " + result + locationString());
  }
  peekedString = null;
  peeked = PEEKED_NONE;
  pathIndices[stackSize - 1]++;
  return result;
}

解决问题1

//通过:Gson.fromJson<Any>(gsonStr, Any::class.java),这种方式解析的数据,如果数据中返回的是Int型数据,
//会自动转化成Double类型

public Object read(JsonReader in) throws IOException {
    JsonToken token = in.peek();
    switch(token) {
    case BEGIN_ARRAY:
        List<Object> list = new ArrayList();
        in.beginArray();

        while(in.hasNext()) {
            list.add(this.read(in));
        }

        in.endArray();
        return list;
    case BEGIN_OBJECT:
        Map<String, Object> map = new LinkedTreeMap();
        in.beginObject();

        while(in.hasNext()) {
            map.put(in.nextName(), this.read(in));
        }

        in.endObject();
        return map;
    case STRING:
        return in.nextString();
    case NUMBER:
        //数字类型,直接转化成double,保障了精度不丢失
        return in.nextDouble();
    case BOOLEAN:
        return in.nextBoolean();
    case NULL:
        in.nextNull();
        return null;
    default:
        throw new IllegalStateException();
    }
}

解决方案可以参考:juejin.cn/post/695394… 原理就是重写其read方法,自定义解析规则,但有个缺点就是如果返回的是3.0会自动转成3,这种场景建议双方约定好解析类型,不用ObjectAdapter去解析

解决问题2

问题原因分析:

//比如3.312230498093283782会转成3.3122306
//但是3.000000498093283782会转成3.0000005
//3.111110498093283782会转成3.1111104
//这里是系统double强转float,至于为什么会出现这样情形。。。。。。母鸡!
public static final TypeAdapter<Number> FLOAT = new TypeAdapter<Number>() {
  @Override
  public Number read(JsonReader in) throws IOException {
    if (in.peek() == JsonToken.NULL) {
      in.nextNull();
      return null;
    }
    //这里强转会丢失精度
    return (float) in.nextDouble();
  }
  @Override
  public void write(JsonWriter out, Number value) throws IOException {
    out.value(value);
  }
};

方案:直接使用double去解析