Android Gson principle analysis

Posted by zab329 on Wed, 24 Nov 2021 21:56:10 +0100

JsonElement

  • abstract class
  • Represents an element of a json string
  • An element:
    • JsonObject
    • JsonArray
    • JsonPrimitive (basic type)
    • JsonNull

Four subclasses of JsonElement

  • JsonObject,JsonArray,JsonPrimitive,JsonNull

JsonPrimitive 

This class encapsulates the basic types of Java and their corresponding object classes. (short and full len gt h, single and double precision, character < expressed as single character string >, Boolean)

Generation of Gson objects

Via new Gson()

  for the Gson object created in this way, Java reflection mechanism will be used to complete json parsing, and a large number of default typeadapterfactories will be added to the factories to generate the default Gson object

Through GsonBuilder

TypeAdapter: the function of this class is to encapsulate the json string into the Java object you specify

Register the TypeAdapter through GsonBuilder and encapsulate the TypeAdapter into a TypeAdpterFactory object

The encapsulated TypeAdapterFactory is passed into the Gson object through the create of GsonBuilder and returned

Call gson.fromjason method and getTypeAdapter method to return your custom Adapter

Next, parse new   Serialization process in Gson() mode

String json = "{ \"name\":\"java book\", \"authors\":[\"Jerry\",\"Tom\"]}";
Book book = new Gson().fromJson(json, Book.class);
String s = new Gson().toJson(book);

public class Book implements Serializable {
    private String name;
    private ArrayList<String> authors;

    public Book(String name, ArrayList<String> authors) {
        this.name = name;
        this.authors = authors;
    }
}

Deserialization process:

public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    boolean isEmpty = true;
   // Accept values of json variables that do not conform to the specification
    boolean oldLenient = reader.isLenient();
    //Force to accept
    reader.setLenient(true);
    try {
//This is equivalent to calling the doPeek() method in JsonReader once, returning the type of the next token without consuming it, and setting the current token
      reader.peek();
      isEmpty = false;
        //TypeToken is essentially an enhanced encapsulation Class of Class
      TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
      //Obtain the corresponding adapter according to the entity type to be converted
      TypeAdapter<T> typeAdapter = getAdapter(typeToken);
      //Generate entity classes through adapters
      T object = typeAdapter.read(reader);
      return object;
    } catch (EOFException e) {

     .......

    }

  }

You can see that the final result is that the read method of the adapter returns the object we need.
First look at the getAdapter (typeToken) method:

public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
    // Judge cache first
    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);
       //Here is the key point. factories is the factory value of the type adapter added when building the gson object  
     
      for (TypeAdapterFactory factory : factories) {
 
       //Loop to find the factory that can handle this type, and factory.create () is very important
        TypeAdapter<T> candidate = factory.create(this, type);
        if (candidate != null) {
          call.setDelegate(candidate);
          typeTokenCache.put(type, candidate);
          return candidate;
        }
      }
      throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
}

        TypeAdapter<T> candidate = factory.create(this, type);according to type Get the corresponding typeAdapter , Then go back, new Gson()In the constructor, you will add a lot of adpter,
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, String datePattern, int dateStyle,
      int timeStyle, List<TypeAdapterFactory> builderFactories,
      List<TypeAdapterFactory> builderHierarchyFactories,
      List<TypeAdapterFactory> factoriesToBeAdded) {
    this.excluder = excluder;
    this.fieldNamingStrategy = fieldNamingStrategy;
    this.instanceCreators = instanceCreators;
    this.constructorConstructor = new ConstructorConstructor(instanceCreators);
    this.serializeNulls = serializeNulls;
    this.complexMapKeySerialization = complexMapKeySerialization;
    this.generateNonExecutableJson = generateNonExecutableGson;
    this.htmlSafe = htmlSafe;
    this.prettyPrinting = prettyPrinting;
    this.lenient = lenient;
    this.serializeSpecialFloatingPointValues = serializeSpecialFloatingPointValues;
    this.longSerializationPolicy = longSerializationPolicy;
    this.datePattern = datePattern;
    this.dateStyle = dateStyle;
    this.timeStyle = timeStyle;
    this.builderFactories = builderFactories;
    this.builderHierarchyFactories = builderHierarchyFactories;
 
    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);
    //Add factory
 
    // 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);
  }

Because we use new   Gson(), because we did not add a custom Adapter, it can only be processed by reflective typeadapterfactory.

Look directly at the create method of ReflectiveTypeAdapterFactory:

@Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
    Class<? super T> raw = type.getRawType();
    //This is used to compare types. If it is not the corresponding type, null will be returned
    //Determines whether the class or interface represented by this * {@ code Class} object is the same as the class or interface represented by the specified {code code}} parameter, or is a superclass or parent interface of the class or interface. If yes, return {@ code true}* Otherwise, {@ code false}
    //Obviously, the return here is true. Our class should be TestMode, which is a class class
    if (!Object.class.isAssignableFrom(raw)) {
      return null; // it's a primitive!
    }
    //Common object construction factory
    ObjectConstructor<T> constructor = constructorConstructor.get(type);
    return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
  }

getBoundFields(gson, type, raw) method to obtain the properties of the class through reflection.

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

debug screenshot:

  You can see that the result returned by the getBoundFields() method result is the map object, which contains the object with key as our class attribute name and value as adapterFactory.

After understanding the getaapper method, let's analyze the read method of 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;
}

 

The field here is the one just obtained in getBoundFields() and placed in the map.

You can see that it calls field.read(in, instance);

return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
    void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException {
        Object fieldValue = field.get(value);
        TypeAdapter t = jsonAdapterPresent ? mapped : new TypeAdapterRuntimeTypeWrapper(context, mapped, fieldType.getType());
        ((TypeAdapter)t).write(writer, fieldValue);
    }

    void read(JsonReader reader, Object value) throws IOException, IllegalAccessException {
        Object fieldValue = mapped.read(reader);
        if (fieldValue != null || !isPrimitive) {
            field.set(value, fieldValue);
        }

    }

    According to the debug screenshot, fieldValue is the value parsed after the typeAdapter calls read, just like the process we are following now. Because the "java book" here is a String, it will be parsed in the Adapter corresponding to the String. If it is a custom class, it will be reflected again. You can see that value is a Book object,

  Both are null, and the field corresponds to the name attribute, so next, the set method sets the fieldValue to the name attribute of the Book object.

  You can see that after the set method, the name of the book object has a value.

Look at the read method. The last returned object is the parsed object,

Serializing toJson also requires reflection,

You can see the write method of ReflectiveTypeAdapterFactory,

@Override public void write(JsonWriter out, T value) throws IOException {
  if (value == null) {
    out.nullValue();
    return;
  }

  out.beginObject();
  try {
    for (BoundField boundField : boundFields.values()) {
      if (boundField.writeField(value)) {
        out.name(boundField.name);
        boundField.write(out, value);
      }
    }
  } catch (IllegalAccessException e) {
    throw new AssertionError(e);
  }
  out.endObject();
}

  It is also the method of calling boundField.write(out, value);

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

  You can see that after write, the out attribute is written with "name": "java book".

  The final result is writer.toString().

 

Topics: Java Android