introduce
Gson is a Java class library provided by Google for mapping between Java objects and JSON data. You can turn a JSON string into a Java object, or vice versa.
The basic concept
-
Serialization: The process of serializing Java objects to Json strings.
-
Deserialization: Deserialization, converting strings into Java objects.
-
There are four types of JsonElements in JSON data:
JsonPrimitive – a subclass of JsonElement that encapsulates the Java primitive type and its corresponding wrapper class and assigns a value to the value using the setValue method.
JsonObject — Json object class that contains key-value pairs. The keys are strings and the value is a JsonElement. Save it with LinkedTreeMap<String, JsonElement> members.
JsonArray — Json arrays are actually Json strings. Use List
to add each element to the Json array
JsonNull — Immutable class
Problems that can be solved
-
Provides a very simple mechanism like toString() and constructors for converting Java objects to and from Json.
-
Allows existing, immutable objects to be converted to Json, or Json to existing objects.
-
Allows you to customize the representation of an object
-
Support arbitrary complex objects
-
String output that generates compressible and readable Json.
Gson deals with several points of the object
It is recommended that all member variables be declared private. 2 It is not necessary to annotate (@expose) whether a field will be serialized or deserialized. All fields contained in the current class (including the parent class) should be serialized or deserialized by default. 3 If a field is transient, it will not be serialized or deserialized. 4 If a field of the object is null, it will not be printed to the Json string. If a synthetic field is synthetic, it will be ignored and should not be serialized or deserialized. 6 Inner classes (or anonymous classes), Or if a field of the local class is the same as a field of the external class, it is ignored and will not be serialized or deserialized
Common notes
@serializedName annotation – Specifies the name of the corresponding field in JSON
@SerializedName("companyId")
private String id;
Copy the code
Expose – Specifies whether the field can be serialized or deserialized. Default is both (true)
@Expose(serialize = false)
private String name;
Copy the code
@ Since – ever Since
@since (1.2) // Since version 1.2 private int shirtNumber;Copy the code
@Until — Until
@until (0.9) // Valid Until 0.9 private String companyName;Copy the code
Gson serialization
Java classes
public class Book { private String[] authors; private String isbn10; private String isbn13; private String title; For code brevity, remove getter and setter methods, etc.}Copy the code
Desired JSON data
{
"title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
"isbn-10": "032133678X",
"isbn-13": "978-0321336781",
"authors": [
"Joshua Bloch",
"Neal Gafter"
]
}
Copy the code
How to convert isBN10 and ISBN13 into ISBN-10 and ISBN-13 required by JSON data? Gson certainly provides the corresponding solution for us. 1. @ SerializedName annotation
public class Book { private String[] authors; @SerializedName("isbn-10") private String isbn10; @SerializedName("isbn-13") private String isbn13; private String title; For code brevity, remove getter and setter methods, etc.}Copy the code
2. Use the JsonSerializer class
public class BookSerialiser implements JsonSerializer { @Override public JsonElement serialize(final Book book, final Type typeOfSrc, final JsonSerializationContext context) { final JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("title", book.getTitle()); jsonObject.addProperty("isbn-10", book.getIsbn10()); jsonObject.addProperty("isbn-13", book.getIsbn13()); final JsonArray jsonAuthorsArray = new JsonArray(); for (final String author : book.getAuthors()) { final JsonPrimitive jsonAuthor = new JsonPrimitive(author); jsonAuthorsArray.add(jsonAuthor); } jsonObject.add("authors", jsonAuthorsArray); return jsonObject; }}Copy the code
- JsonSerializer is an interface that we need to provide our own implementation to meet our serialization requirements.
public interface JsonSerializer<T> {
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
}
Copy the code
- What we need to create is a JsonElement object, in this case Book is an object, so create a JsonObject.
- We then populate the jsonObject with the data from the corresponding fields.
- So it returns a JsonElement type, in this case a jsonObject. Complete javaBean->JSON data conversion.
gsonBuilder.registerTypeAdapter(Book.class, new BookSerialiser())
Method Configure JsonSerializer. By calling thegsonBuilder.setPrettyPrinting();
The method also tells Gson to format the generated JSON object.
Gson gson = new GsonBuilder() .registerTypeAdapter(Book.class, new BookSerialiser()) .enableComplexMapKeySerialization() .serializeNulls() .setDateFormat(DateFormat.LONG) SetFieldNamingPolicy (FieldNamingPolicy. UPPER_CAMEL_CASE). SetPrettyPrinting () setVersion (1.0). The create ();Copy the code
Gson deserialization
Json string
{
"title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
"isbn-10": "032133678X",
"isbn-13": "978-0321336781",
"authors": [
"Joshua Bloch",
"Neal Gafter"
]
}
Copy the code
Target: Book object
Scenario 1: Annotate @serializedName
public class Book { private String[] authors; @SerializedName("isbn-10") private String isbn10; @SerializedName(value = "isbn-13", alternate = {"isbn13","isbn.13"}) private String isbn13; private String title; For code brevity, remove getter and setter methods, etc.}Copy the code
We use an alternate field in the @serializedName annotation. Value is the default field and alternate is available for both serialization and deserialization. Alternate only works for deserialization. In other words, when the server returns JSON data to us, it may have the same image, which means “image”,”img”,”icon”, etc. We can solve this problem by using the alternate field in @serializedName, and convert all of them into the image field in our entity class.
Solution 2: Use JsonDeserializer
public class BookDeserializer implements JsonDeserializer<Book> { @Override public Book deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException { final JsonObject jsonObject = json.getAsJsonObject(); final JsonElement jsonTitle = jsonObject.get("title"); final String title = jsonTitle.getAsString(); final String isbn10 = jsonObject.get("isbn-10").getAsString(); final String isbn13 = jsonObject.get("isbn-13").getAsString(); final JsonArray jsonAuthorsArray = jsonObject.get("authors").getAsJsonArray(); final String[] authors = new String[jsonAuthorsArray.size()]; for (int i = 0; i < authors.length; i++) { final JsonElement jsonAuthor = jsonAuthorsArray.get(i); authors[i] = jsonAuthor.getAsString(); } final Book book = new Book(); book.setTitle(title); book.setIsbn10(isbn10); book.setIsbn13(isbn13); book.setAuthors(authors); return book; }}Copy the code
- through
final JsonObject jsonObject = json.getAsJsonObject();
Convert our JsonElement to a JsonObject - through
jsonObject.get("xxx").getAsString()
Gets the value of the corresponding String - through
jsonObject.get("xx").getAsJsonArray();
Get the corresponding JSON array and iterate through the corresponding field values - Set the obtained value to the Book class using setter methods.
- The object instance of Book is returned. Complete JSON->javaBean conversion
gsonBuilder.registerTypeAdapter(Book.class, new BookSerialiser())
Method Configure JsonSerializer.- Read Json data from a local stream
InputStreamReader
Complete)
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Book.class, new BookDeserializer());
Gson gson = gsonBuilder.create();
// The JSON data
try(Reader reader = new InputStreamReader(Main.class.getResourceAsStream("/part1/sample.json"), "UTF-8")){
// Parse JSON to Java
Book book = gson.fromJson(reader, Book.class);
System.out.println(book);
}
Copy the code
TypeAdapter introduction
We use this middleware when converting Java objects into JSON stringsJsonElement
And theTypeAdapter
It is by removing this middle layer that data is parsed directly by stream, which greatly improves the efficiency of parsing.
TypeAdapter, as an abstract class, provides two abstract methods. The write() and read() methods, respectively, also correspond to serialization and deserialization.
TypeAdapter small demo
->> Book Entity class
public class Book { private String[] authors; private String isbn; private String title; For code brevity, remove getter and setter methods, etc.}Copy the code
->> BookTypeAdapter Specifies the TypeAdapter class for serialization and deserialization
public class BookTypeAdapter extends TypeAdapter { @Override public Book read(final JsonReader in) throws IOException { final Book book = new Book(); in.beginObject(); while (in.hasNext()) { switch (in.nextName()) { case "isbn": book.setIsbn(in.nextString()); break; case "title": book.setTitle(in.nextString()); break; case "authors": book.setAuthors(in.nextString().split(";" )); break; } } in.endObject(); return book; } @Override public void write(final JsonWriter out, final Book book) throws IOException { out.beginObject(); out.name("isbn").value(book.getIsbn()); out.name("title").value(book.getTitle()); out.name("authors").value(StringUtils.join(book.getAuthors(), ";" )); out.endObject(); }}Copy the code
– > > registration
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Book.class, new BookTypeAdapter());
final Gson gson = gsonBuilder.create();
Copy the code
The role of JsonReader
The JSON string is read and converted to a token stream from which JsonParser parses JSON and converts Java objects.
TypeToken
Java generics have type erasure at run time, and Gson parsing will not get the expected type. TypeToken solves this problem. The solution: anonymous classes + reflection.
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(); } static Type getSuperclassTypeParameter(Class<? > subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]); }Copy the code
The idea is to take an anonymous class that inherits TypeToken, get the generic superclass of that anonymous class, and then cast that generic superclass to ParameterizedType. ParameterizedType contains the method getActualTypeArguments() to get its actual type parameters and the method getRawType() to get its original type.
Public class TypeTokenTest {// json string: {"data":"data from server"} public class Response<T>{public T data; @override public String toString() {return "Response{" + "data=" + data + '}'; @override public String toString() {return "Response{" + "data=" + data + '}'; } } private Response<String> data; public static void main(String[] args) { String json = "{\"data\":\"data from server\"}"; //fromJson(String json, Class<T> classOfT) expects the type Response<String>. //Class responsecass = Response<String>. / But this does not compile, because Response<String> is not a Class type. Response<String> result = (Response<String>) new Gson(). FromJson (json, responass); System.out.println("result=" + result); //Gson solution Type Type = new TypeToken<Response<String>>(){}.getType(); System.out.println("type=" + type); //TypeTokenTest$Response<java.lang.String> result = new Gson().fromJson(json, type); System.out.println("result=" + result); }}Copy the code
New TypeToken<Response>(){}
1. class TypeToken$0 extends TypeToken<Response<String>>{ }
2. TypeToken typeToken = new TypeToken$0();
Copy the code
TypeToken 0 is an anonymous subclass of TypeToken
>. So typeToken is TypeToken0 is an anonymous subclass of typeToken
>. So typeToken is TypeToken0 is an anonymous subclass of typeToken
>. So typeToken is TypeToken0, the parent type is typeToken
, Instead of TypeToken, Type superclass. = ttf_subclass getGenericSuperclass (), did you get the superclass of TypeToken < Response >, instead of TypeToken. Continue to call the parameterized. The getActualTypeArguments () [0] get a Response
That’s why the TypeToken constructor is protected. Public methods that want to call the class cannot simply new a TypeToken object. You can create a new anonymous class (new TypeToken(){}) or create a new class that inherits TypeToken to get an instance of that class and then call the public method of that class
The write method
public void write(final JsonWriter out, final Book book) throws IOException { out.beginObject(); out.name("isbn").value(book.getIsbn()); out.name("title").value(book.getTitle()); out.name("authors").value(StringUtils.join(book.getAuthors(), ";" )); out.endObject(); }Copy the code
Pass in the JsonWriter, and an instance of the Book object to be serialized, and write to the JsonWriter in a similar way to PrintStream.
out.beginObject()
produce{
If we want to generate an array object, use the correspondingbeginArray()
public JsonWriter beginObject() throws IOException {
writeDeferredName();
return open(EMPTY_OBJECT, "{");
}
Copy the code
out.name("isbn").value(book.getIsbn()); out.name("title").value(book.getTitle());
Get the ISbn and title fields in the book and set them to the ISbn and title fields in the Json object.- out.name(“authors”).value(StringUtils.join(book.getAuthors(), “;” ));
out.endObject()
Corresponding to the}
The last of the json
{ "isbn": "978-0321336781", "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases", "authors": "Joshua Bloch; Neal Gafter" }Copy the code
The read method
Pass in an instance of a JsonReader object and return the deserialized object.
public Book read(final JsonReader in) throws IOException { final Book book = new Book(); in.beginObject(); while (in.hasNext()) { switch (in.nextName()) { case "isbn": book.setIsbn(in.nextString()); break; case "title": book.setTitle(in.nextString()); break; case "authors": book.setAuthors(in.nextString().split(";" )); break; } } in.endObject(); return book; }Copy the code
- Again through
in.beginObject();
andin.endObject();
Corresponding analytical{
.}
- A while loop iterates through each JsonElement to set the corresponding value for the entity class
How TypeAdapter works
- Register a TypeAdapter with GsonBuilder and encapsulate the TypeAdapter as a TypeAdpterFactory object
- Pass the encapsulated TypeAdapterFactory into a Gson object through the GsonBuilder create method
- Call the gson.fromjson method, call the getAdapter method to return the custom Adapter, and call its Reader method to turn the JSON into a Java object
TypeAdapterFactory
There are a number of TypeAdapterFactories in Gson, such as one TypeAdapter for each basic type and one TypeAdapterFactory for each TypeAdapter
public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() { @Override public Number read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } try { return in.nextInt(); } catch (NumberFormatException e) { throw new JsonSyntaxException(e); } } @Override public void write(JsonWriter out, Number value) throws IOException { out.value(value); }}; public static final TypeAdapterFactory INTEGER_FACTORY = newFactory(int.class, Integer.class, INTEGER); public static final TypeAdapter<AtomicInteger> ATOMIC_INTEGER = new TypeAdapter<AtomicInteger>() { @Override public AtomicInteger read(JsonReader in) throws IOException { try { return new AtomicInteger(in.nextInt()); } catch (NumberFormatException e) { throw new JsonSyntaxException(e); }}Copy the code
A custom TypeAdapter is essentially converted to a TypeAdapterFactory and then iterated through the TyperAdapterFactory in Gson’s getAdapter method and called the Create method to get the TypeAdapter
public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
factories.add(factory);
return this;
}
Copy the code
When new Gson() is the Gson default constructor, another constructor with arguments is called for initialization
public Gson() { this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, Collections.<Type, InstanceCreator<? >>emptyMap(), DEFAULT_SERIALIZE_NULLS, DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML, DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, Collections.<TypeAdapterFactory>emptyList(), Collections.<TypeAdapterFactory>emptyList(), Collections.<TypeAdapterFactory>emptyList()); }Copy the code
One of the functions of the constructor with arguments is to add a large number of Gson typeAdapterFactories to the Factories of Gson objects:
// 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);
Copy the code
Add TypeAdapterFactory order is to add user custom TypeAdapterFactory first, then add Gson own TypeAdapterFactory, add ReflectiveTypeAdapterFactory finally. Gson gets the TypeAdapterFactory in this order when parsing
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) { ... 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; }}... }Copy the code
GetAdapter method will traverse the factories, according to the type to get TypeAdapterFactory, in the for loop as long as TypeAdapterFactory. Create return TypeAdapter! =null returns the created TypeAdapter that parses json, because the user-defined TypeAdapterFactory and Gson’s built-in TypeAdapterFactory are in front of the factories, In preference to ReflectiveTypeAdapterFactory traversal, so avoids the use Gson reflection mechanism to parse the json.
ReflectiveTypeAdapterFactory
Call ReflectiveTypeAdapterFactory the create method, return a ReflectiveTypeAdapterFactory Adapter object
@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)); }Copy the code
ObjectConstructor
The ObjectConstructor object is used by Adapter to construct objects as it parses JSON. And the ObjectConstructor is an object from a constructorConstructor, which means building a constructor, which is used to create an ObjectConstructor object.
ConstructorConstructor passes in an instanceCreators object, which is a meta-object. By default, this object is empty and can be injected if you need to construct an object of your own. So, Gson will take precedence over the ObjectConstructor you inject to transform the final object. Let’s look at the ObjectConstructor get(TypeToken TypeToken) method for ConstructorConstructor:
public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) { final Type type = typeToken.getType(); final Class<? super T> rawType = typeToken.getRawType(); // Get the InstanceCreator from type. If you get the InstanceCreator, // First try an instance Creator @SuppressWarnings("unchecked") // Types must agree final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type); if (typeCreator ! = null) { return new ObjectConstructor<T>() { @Override public T construct() { return typeCreator.createInstance(type); }}; } // Use rawType to get InstanceCreator. // Next try raw Type match for instance Creators @suppressWarnings ("unchecked") // Types must agree final InstanceCreator<T> rawTypeCreator = (InstanceCreator<T>) instanceCreators.get(rawType); if (rawTypeCreator ! = null) { return new ObjectConstructor<T>() { @Override public T construct() { return rawTypeCreator.createInstance(type); }}; } // if it is a normal class, a defaultConstructor object is created using the newDefaultConstructor method ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType); if (defaultConstructor ! = null) { return defaultConstructor; } // If it is an interface class, Through newDefaultImplementationConstructor method creates a default constructor object ObjectConstructor < T > defaultImplementation = newDefaultImplementationConstructor(type, rawType); if (defaultImplementation ! = null) { return defaultImplementation; } // If there is no default constructor in a custom Java class, // finally try unsafe return newUnsafeAllocator(type, rawType); } private <T> ObjectConstructor<T> newDefaultConstructor(Class<? Super T> rawType) {try {// This method returns a Constructor object with a list of specified arguments, where no arguments are passed, i.e. the default Constructor<? super T> constructor = rawType.getDeclaredConstructor(); if (! constructor.isAccessible()) { accessor.makeAccessible(constructor); } return new ObjectConstructor<T>() { @SuppressWarnings("unchecked") // T is the same raw type as is requested @Override public T construct() { try { Object[] args = null; return (T) constructor.newInstance(args); } Catch (InstantiationException e) {// TODO: JsonParseException? throw new RuntimeException("Failed to invoke " + constructor + " with no args", e); } catch (InvocationTargetException e) { // TODO: don't wrap if cause is unchecked! // TODO: JsonParseException ? throw new RuntimeException("Failed to invoke " + constructor + " with no args", e.getTargetException()); } catch (IllegalAccessException e) { throw new AssertionError(e); }}}; } catch (NoSuchMethodException e) { return null; }}Copy the code
GetBoundFields method
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; } 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); } @Override void read(JsonReader reader, Object value) throws IOException, IllegalAccessException {// Fetch the corresponding TypeAdapter according to the Type of this field. Call TypeAdapter's read to read fieldValue Object fieldValue = typeAdapter.read(reader); if (fieldValue ! = null || ! isPrimitive) { 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 } }; }Copy the code
The BoundField class is used to bind the Field name, either the Field name or a name defined by an annotation, to the Field.
Gson with ReflectiveTypeAdapterFactory through reflection parsing json, core principles through the reflection to create Java objects in a nutshell, iterate over the object Field, And through the Field set(Object,value) method to the Java Object Field assignment. Steps:
- Gets the default constructor for type
- Create a Java object of type Type using Constructor newInstance()
- Parse json key-value pairs to get keys and values,
- Get the Field corresponding to the key, and set the value through the set(Object, value) method of the Field
summary
How Gson resolves to Java objects:
2) Encapsulate the TypeAdapter as a TypeAdapterFactory and add the Factory to Gson’s Factories (List) Finally call getAdapter with the fromJson method, traverse the factories, 4) Call TypeAdapter’s read method to complete the json to Java Object conversion.