Fix typos and improve Gson Design Document formatting (#2491)

* Fix typos and grammar issues

Most of them were found by IntelliJ

* Improve formatting and fix typos in Gson Design Document
This commit is contained in:
Marcono1234 2023-09-21 00:44:15 +02:00 committed by GitHub
parent 78e4bdaede
commit e93fda9f17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 49 additions and 45 deletions

View File

@ -1,57 +1,59 @@
# Gson Design Document
This document presents issues that we faced while designing Gson. It is meant for advanced users or developers working on Gson. If you are interested in learning how to use Gson, see its user guide.
This document presents issues that we faced while designing Gson. It is meant for advanced users or developers working on Gson. If you are interested in learning how to use Gson, see its user guide.
**Navigating the Json tree or the target Type Tree while deserializing**
Some information in this document is outdated and does not reflect the current state of Gson. This information can however still be relevant for understanding the history of Gson.
When you are deserializing a Json string into an object of desired type, you can either navigate the tree of the input, or the type tree of the desired type. Gson uses the latter approach of navigating the type of the target object. This keeps you in tight control of instantiating only the type of objects that you are expecting (essentially validating the input against the expected "schema"). By doing this, you also ignore any extra fields that the Json input has but were not expected.
## Navigating the Json tree or the target Type Tree while deserializing
As part of Gson, we wrote a general purpose ObjectNavigator that can take any object and navigate through its fields calling a visitor of your choice.
When you are deserializing a Json string into an object of desired type, you can either navigate the tree of the input, or the type tree of the desired type. Gson uses the latter approach of navigating the type of the target object. This keeps you in tight control of instantiating only the type of objects that you are expecting (essentially validating the input against the expected "schema"). By doing this, you also ignore any extra fields that the Json input has but were not expected.
**Supporting richer serialization semantics than deserialization semantics**
As part of Gson, we wrote a general purpose ObjectNavigator that can take any object and navigate through its fields calling a visitor of your choice.
Gson supports serialization of arbitrary collections, but can only deserialize genericized collections. this means that Gson can, in some cases, fail to deserialize Json that it wrote. This is primarily a limitation of the Java type system since when you encounter a Json array of arbitrary types there is no way to detect the types of individual elements. We could have chosen to restrict the serialization to support only generic collections, but chose not to.This is because often the user of the library are concerned with either serialization or deserialization, but not both. In such cases, there is no need to artificially restrict the serialization capabilities.
## Supporting richer serialization semantics than deserialization semantics
**Supporting serialization and deserialization of classes that are not under your control and hence can not be modified**
Gson supports serialization of arbitrary collections, but can only deserialize genericized collections. this means that Gson can, in some cases, fail to deserialize Json that it wrote. This is primarily a limitation of the Java type system since when you encounter a Json array of arbitrary types there is no way to detect the types of individual elements. We could have chosen to restrict the serialization to support only generic collections, but chose not to. This is because often the user of the library are concerned with either serialization or deserialization, but not both. In such cases, there is no need to artificially restrict the serialization capabilities.
Some Json libraries use annotations on fields or methods to indicate which fields should be used for Json serialization. That approach essentially precludes the use of classes from JDK or third-party libraries. We solved this problem by defining the notion of Custom serializers and deserializers. This approach is not new, and was used by the JAX-RPC technology to solve essentially the same problem.
## Supporting serialization and deserialization of classes that are not under your control and hence can not be modified
**Using Checked vs Unchecked exceptions to indicate a parsing error**
Some Json libraries use annotations on fields or methods to indicate which fields should be used for Json serialization. That approach essentially precludes the use of classes from JDK or third-party libraries. We solved this problem by defining the notion of custom serializers and deserializers. This approach is not new, and was used by the JAX-RPC technology to solve essentially the same problem.
We chose to use unchecked exceptions to indicate a parsing failure. This is primarily done because usually the client can not recover from bad input, and hence forcing them to catch a checked exception results in sloppy code in the catch() block.
## Using Checked vs Unchecked exceptions to indicate a parsing error
**Creating class instances for deserialization**
We chose to use unchecked exceptions to indicate a parsing failure. This is primarily done because usually the client can not recover from bad input, and hence forcing them to catch a checked exception results in sloppy code in the `catch()` block.
Gson needs to create a dummy class instance before it can deserialize Json data into its fields. We could have used Guice to get such an instance, but that would have resulted in a dependency on Guice. Moreover, it probably would have done the wrong thing since Guice is expected to return a valid instance, whereas we need to create a dummy one. Worse, Gson would overwrite the fields of that instance with the incoming data there by modifying the instance for all subsequent Guice injections. This is clearly not a desired behavior. Hence, we create class instances by invoking the parameterless constructor. We also handle the primitive types, enums, collections, sets, maps and trees as a special case.
## Creating class instances for deserialization
To solve the problem of supporting unmodifiable types, we use custom instance creators. So, if you want to use a library types that does not define a default constructor (for example, Money class), then you can register an instance creator that returns a dummy instance when asked.
Gson needs to create a dummy class instance before it can deserialize Json data into its fields. We could have used Guice to get such an instance, but that would have resulted in a dependency on Guice. Moreover, it probably would have done the wrong thing since Guice is expected to return a valid instance, whereas we need to create a dummy one. Worse, Gson would overwrite the fields of that instance with the incoming data thereby modifying the instance for all subsequent Guice injections. This is clearly not a desired behavior. Hence, we create class instances by invoking the parameterless constructor. We also handle the primitive types, enums, collections, sets, maps and trees as a special case.
**Using fields vs getters to indicate Json elements**
To solve the problem of supporting unmodifiable types, we use custom instance creators. So, if you want to use a library type that does not define a default constructor (for example, `Money` class), then you can register an instance creator that returns a dummy instance when asked.
Some Json libraries use the getters of a type to deduce the Json elements. We chose to use all fields (up the inheritance hierarchy) that are not transient, static, or synthetic. We did this because not all classes are written with suitably named getters. Moreover, getXXX or isXXX might be semantic rather than indicating properties.
## Using fields vs getters to indicate Json elements
However, there are good arguments to support properties as well. We intend to enhance Gson in a latter version to support properties as an alternate mapping for indicating Json fields. For now, Gson is fields-based.
Some Json libraries use the getters of a type to deduce the Json elements. We chose to use all fields (up the inheritance hierarchy) that are not transient, static, or synthetic. We did this because not all classes are written with suitably named getters. Moreover, `getXXX` or `isXXX` might be semantic rather than indicating properties.
**Why are most classes in Gson marked as final?**
However, there are good arguments to support properties as well. We intend to enhance Gson in a later version to support properties as an alternate mapping for indicating Json fields. For now, Gson is fields-based.
While Gson provides a fairly extensible architecture by providing pluggable serializers and deserializers, Gson classes were not specifically designed to be extensible. Providing non-final classes would have allowed a user to legitimately extend Gson classes, and then expect that behavior to work in all subsequent revisions. We chose to limit such use-cases by marking classes as final, and waiting until a good use-case emerges to allow extensibility. Marking a class final also has a minor benefit of providing additional optimization opportunities to Java compiler and virtual machine.
## Why are most classes in Gson marked as final?
**Why are inner interfaces and classes used heavily in Gson?**
While Gson provides a fairly extensible architecture by providing pluggable serializers and deserializers, Gson classes were not specifically designed to be extensible. Providing non-final classes would have allowed a user to legitimately extend Gson classes, and then expect that behavior to work in all subsequent revisions. We chose to limit such use-cases by marking classes as final, and waiting until a good use-case emerges to allow extensibility. Marking a class final also has a minor benefit of providing additional optimization opportunities to Java compiler and virtual machine.
Gson uses inner classes substantially. Many of the public interfaces are inner interfaces too (see JsonSerializer.Context or JsonDeserializer.Context as an example). These are primarily done as a matter of style. For example, we could have moved JsonSerializer.Context to be a top-level class JsonSerializerContext, but chose not to do so. However, if you can give us good reasons to rename it alternately, we are open to changing this philosophy.
## Why are inner interfaces and classes used heavily in Gson?
**Why do you provide two ways of constructing Gson?**
Gson uses inner classes substantially. Many of the public interfaces are inner interfaces too (see `JsonSerializer.Context` or `JsonDeserializer.Context` as an example). These are primarily done as a matter of style. For example, we could have moved `JsonSerializer.Context` to be a top-level class `JsonSerializerContext`, but chose not to do so. However, if you can give us good reasons to rename it alternately, we are open to changing this philosophy.
Gson can be constructed in two ways: by invoking new Gson() or by using a GsonBuilder. We chose to provide a simple no-args constructor to handle simple use-cases for Gson where you want to use default options, and quickly want to get going with writing code. For all other situations, where you need to configure Gson with options such as formatters, version controls etc, we use a builder pattern. The builder pattern allows a user to specify multiple optional settings for what essentially become constructor parameters for Gson.
## Why do you provide two ways of constructing Gson?
**Comparing Gson with Alternate Approaches**
Gson can be constructed in two ways: by invoking `new Gson()` or by using a `GsonBuilder`. We chose to provide a simple no-args constructor to handle simple use-cases for Gson where you want to use default options, and quickly want to get going with writing code. For all other situations, where you need to configure Gson with options such as formatters, version controls etc., we use a builder pattern. The builder pattern allows a user to specify multiple optional settings for what essentially become constructor parameters for Gson.
## Comparing Gson with alternate approaches
Note that these comparisons were done while developing Gson so these date back to mid to late 2007.
__Comparing Gson with org.json library__
### Comparing Gson with org.json library
org.json is a much lower-level library that can be used to write a toJson() method in a class. If you can not use Gson directly (may be because of platform restrictions regarding reflection), you could use org.json to hand-code a toJson method in each object.
org.json is a much lower-level library that can be used to write a `toJson()` method in a class. If you can not use Gson directly (maybe because of platform restrictions regarding reflection), you could use org.json to hand-code a `toJson` method in each object.
__Comparing Gson with org.json.simple library__
### Comparing Gson with org.json.simple library
org.json.simple library is very similar to org.json library and hence fairly low level. The key issue with this library is that it does not handle exceptions very well. In some cases it appeared to just eat the exception while in other cases it throws an "Error" rather than an exception.

View File

@ -66,7 +66,7 @@ Or in case this occurs for a field in one of your classes which you did not actu
**Symptom:** You released a new version of your Android app and it fails to parse JSON data created by the previous version of your app
**Reason:** You probably have not configured ProGuard / R8 correctly; probably the fields names are being obfuscated and their naming changed between the versions of your app
**Reason:** You probably have not configured ProGuard / R8 correctly; probably the field names are being obfuscated and their naming changed between the versions of your app
**Solution:** Make sure you have configured ProGuard / R8 correctly to preserve the names of your fields. See the [Android example](examples/android-proguard-example/README.md) for more information.
@ -137,7 +137,7 @@ To spot syntax errors in the JSON data easily you can open it in an editor with
**Symptom:** JSON data contains an integral number such as `45` but Gson returns it as `double`
**Reason:** When parsing a JSON number as `Object`, Gson will by default create always return a `double`
**Reason:** When parsing a JSON number as `Object`, Gson will by default always return a `double`
**Solution:** Use [`GsonBuilder.setObjectToNumberStrategy`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/GsonBuilder.html#setObjectToNumberStrategy(com.google.gson.ToNumberStrategy)) to specify what type of number should be returned

View File

@ -366,7 +366,7 @@ class Event {
You can serialize the collection with Gson without doing anything specific: `toJson(collection)` would write out the desired output.
However, deserialization with `fromJson(json, Collection.class)` will not work since Gson has no way of knowing how to map the input to the types. Gson requires that you provide a genericised version of collection type in `fromJson()`. So, you have three options:
However, deserialization with `fromJson(json, Collection.class)` will not work since Gson has no way of knowing how to map the input to the types. Gson requires that you provide a genericized version of the collection type in `fromJson()`. So, you have three options:
1. Use Gson's parser API (low-level streaming parser or the DOM parser JsonParser) to parse the array elements and then use `Gson.fromJson()` on each of the array elements.This is the preferred approach. [Here is an example](extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java) that demonstrates how to do this.
@ -389,7 +389,7 @@ You can also find source code for some commonly used classes such as JodaTime at
### Custom Serialization and Deserialization
Sometimes default representation is not what you want. This is often the case when dealing with library classes (DateTime, etc).
Sometimes the default representation is not what you want. This is often the case when dealing with library classes (DateTime, etc.).
Gson allows you to register your own custom serializers and deserializers. This is done by defining two parts:
* JSON Serializers: Need to define custom serialization for an object
@ -741,7 +741,7 @@ In addition Gson's object model and data binding, you can use Gson to read from
## Issues in Designing Gson
See the [Gson design document](GsonDesignDocument.md "Gson design document") for a discussion of issues we faced while designing Gson. It also include a comparison of Gson with other Java libraries that can be used for JSON conversion.
See the [Gson design document](GsonDesignDocument.md "Gson design document") for a discussion of issues we faced while designing Gson. It also includes a comparison of Gson with other Java libraries that can be used for JSON conversion.
## Future Enhancements to Gson

View File

@ -64,6 +64,8 @@ public final class FieldAttributes {
}
/**
* Returns the declared generic type of the field.
*
* <p>For example, assume the following class definition:
* <pre class="code">
* public class Foo {
@ -104,7 +106,7 @@ public final class FieldAttributes {
}
/**
* Return the {@code T} annotation object from this field if it exist; otherwise returns
* Returns the {@code T} annotation object from this field if it exists; otherwise returns
* {@code null}.
*
* @param annotation the class of the annotation that will be retrieved
@ -115,7 +117,7 @@ public final class FieldAttributes {
}
/**
* Return the annotations that are present on this field.
* Returns the annotations that are present on this field.
*
* @return an array of all the annotations set on the field
* @since 1.4

View File

@ -601,7 +601,7 @@ public final class Gson {
/**
* This method is used to get an alternate type adapter for the specified type. This is used
* to access a type adapter that is overridden by a {@link TypeAdapterFactory} that you
* may have registered. This features is typically used when you want to register a type
* may have registered. This feature is typically used when you want to register a type
* adapter that does a little bit of work but then delegates further processing to the Gson
* default type adapter. Here is an example:
* <p>Let's say we want to write a type adapter that counts the number of objects being read
@ -635,7 +635,7 @@ public final class Gson {
* System.out.println("Num JSON writes: " + stats.numWrites);
* }</pre>
* Note that this call will skip all factories registered before {@code skipPast}. In case of
* multiple TypeAdapterFactories registered it is up to the caller of this function to insure
* multiple TypeAdapterFactories registered it is up to the caller of this function to ensure
* that the order of registration does not prevent this method from reaching a factory they
* would expect to reply from this call.
* Note that since you can not override the type adapter factories for some types, see
@ -680,7 +680,7 @@ public final class Gson {
}
if (skipPastFound) {
throw new IllegalArgumentException("GSON cannot serialize " + type);
throw new IllegalArgumentException("GSON cannot serialize or deserialize " + type);
} else {
// Probably a factory from @JsonAdapter on a field
return getAdapter(type);
@ -1019,7 +1019,7 @@ public final class Gson {
* This method deserializes the specified JSON into an object of the specified class. It is not
* suitable to use if the specified class is a generic type since it will not have the generic
* type information because of the Type Erasure feature of Java. Therefore, this method should not
* be used if the desired type is a generic type. Note that this method works fine if the any of
* be used if the desired type is a generic type. Note that this method works fine if any of
* the fields of the specified object are generics, just the object itself should not be a
* generic type. For the cases when the object is of generic type, invoke
* {@link #fromJson(String, TypeToken)}. If you have the JSON in a {@link Reader} instead of

View File

@ -225,7 +225,7 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
*
* @param i the index of the element that is being sought.
* @return the element present at the i-th index.
* @throws IndexOutOfBoundsException if i is negative or greater than or equal to the
* @throws IndexOutOfBoundsException if {@code i} is negative or greater than or equal to the
* {@link #size()} of the array.
*/
public JsonElement get(int i) {

View File

@ -63,7 +63,7 @@ import java.lang.annotation.Target;
public @interface Until {
/**
* The value indicating a version number until this member or type should be be included.
* The value indicating a version number until this member or type should be included.
* The number is exclusive; annotated elements will be included if {@code gsonVersion < value}.
*/
double value();

View File

@ -36,7 +36,7 @@ import java.lang.reflect.Type;
/**
* Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the
* tree adapter may be serialization-only or deserialization-only, this class
* has a facility to lookup a delegate type adapter on demand.
* has a facility to look up a delegate type adapter on demand.
*/
public final class TreeTypeAdapter<T> extends SerializationDelegatingTypeAdapter<T> {
private final JsonSerializer<T> serializer;

View File

@ -45,7 +45,7 @@ final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
// Order of preference for choosing type adapters
// First preference: a type adapter registered for the runtime type
// Second preference: a type adapter registered for the declared type
// Third preference: reflective type adapter for the runtime type (if it is a sub class of the declared type)
// Third preference: reflective type adapter for the runtime type (if it is a subclass of the declared type)
// Fourth preference: reflective type adapter for the declared type
TypeAdapter<T> chosen = delegate;

View File

@ -686,7 +686,7 @@ public class JsonReader implements Closeable {
return PEEKED_NONE;
}
// Upper cased keywords are not allowed in STRICT mode
// Uppercased keywords are not allowed in STRICT mode
boolean allowsUpperCased = strictness != Strictness.STRICT;
// Confirm that chars [0..length) match the keyword.
@ -1745,7 +1745,7 @@ public class JsonReader implements Closeable {
* Consumes the non-execute prefix if it exists.
*/
private void consumeNonExecutePrefix() throws IOException {
// fast forward through the leading whitespace
// fast-forward through the leading whitespace
int unused = nextNonWhitespace(true);
pos--;

View File

@ -176,7 +176,7 @@ public class EnumTest {
Type type = new TypeToken<EnumSet<Roshambo>>() {}.getType();
EnumSet<Roshambo> bar = gson.fromJson(json, type);
assertThat(bar).containsExactly(Roshambo.ROCK, Roshambo.PAPER).inOrder();
assertThat(bar).doesNotContain(Roshambo.SCISSORS);;
assertThat(bar).doesNotContain(Roshambo.SCISSORS);
}
@Test

View File

@ -270,7 +270,7 @@ public final class ParseBenchmark {
}
private static class GsonBindParser implements Parser {
private static Gson gson = new GsonBuilder()
private static final Gson gson = new GsonBuilder()
.setDateFormat("EEE MMM dd HH:mm:ss Z yyyy")
.create();