Add Gson.fromJson(..., TypeToken) overloads (#1700)

* Add Gson.fromJson(..., TypeToken) overloads

Previously only Gson.fromJson(..., Type) existed which is however not
type-safe since the generic type parameter T used for the return type is
not bound.
Since these methods are often used in the form
  gson.fromJson(..., new TypeToken<...>(){}.getType())
this commit now adds overloads which accept a TypeToken and are therefore
more type-safe.

Additional changes:
- Fixed some grammar mistakes
- Added javadoc @see tags
- Consistently write "JSON" in uppercase
- More precise placement of @SuppressWarnings("unchecked")

* Add to Gson.fromJson javadoc that JSON is fully consumed

The newly added documentation deliberately does not state which exception
is thrown because Gson.assertFullConsumption could throw either a
JsonIOException or a JsonSyntaxException.

* Remove unnecessary wrapping and unwrapping as TypeToken in Gson.fromJson

Since the actual implementation of Gson.fromJson is TypeToken based, the
TypeToken variant overloads are now the "main" implementation and the other
overloads delegate to them.
Previously the Type variant overloads were the "main" implementation which
caused `TypeToken.getType()` followed by `TypeToken.get(...)` when the
TypeToken variant overloads were used.

* Trim source code whitespaces

* Fix Gson.fromJson(JsonReader, Class) not casting read Object

To be consistent with the other Gson.fromJson(..., Class) overloads the
method should cast the result.

* Replace User Guide link in Gson documentation

* Remove more references to fromJson(..., Type)

* Extend documentation for fromJson(JsonReader, ...)

* Replace some TypeToken.getType() usages

* Address feedback; improve documentation

* Remove fromJson(JsonReader, Class) again

As noticed during review adding this method is source incompatible.
This commit is contained in:
Marcono1234 2022-09-19 15:47:11 +02:00 committed by GitHub
parent a733150cfa
commit 6b9db2e449
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 364 additions and 111 deletions

View File

@ -225,7 +225,7 @@ Collection<Integer> ints = Arrays.asList(1,2,3,4,5);
String json = gson.toJson(ints); // ==> json is [1,2,3,4,5]
// Deserialization
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
TypeToken<Collection<Integer>> collectionType = new TypeToken<Collection<Integer>>(){};
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
// ==> ints2 is same as ints
```
@ -263,7 +263,7 @@ For deserialization Gson uses the `read` method of the `TypeAdapter` registered
```java
Gson gson = new Gson();
Type mapType = new TypeToken<Map<String, String>>(){}.getType();
TypeToken<Map<String, String>> mapType = new TypeToken<Map<String, String>>(){};
String json = "{\"key\": \"value\"}";
// Deserialization

View File

@ -76,26 +76,32 @@ import java.util.concurrent.atomic.AtomicLongArray;
* <pre>
* Gson gson = new Gson(); // Or use new GsonBuilder().create();
* MyType target = new MyType();
* String json = gson.toJson(target); // serializes target to Json
* String json = gson.toJson(target); // serializes target to JSON
* MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
* </pre>
*
* <p>If the object that your are serializing/deserializing is a {@code ParameterizedType}
* (i.e. contains at least one type parameter and may be an array) then you must use the
* {@link #toJson(Object, Type)} or {@link #fromJson(String, Type)} method. Here is an
* example for serializing and deserializing a {@code ParameterizedType}:
*
* <p>If the type of the object that you are converting is a {@code ParameterizedType}
* (i.e. has at least one type argument, for example {@code List<MyType>}) then for
* deserialization you must use a {@code fromJson} method with {@link Type} or {@link TypeToken}
* parameter to specify the parameterized type. For serialization specifying a {@code Type}
* or {@code TypeToken} is optional, otherwise Gson will use the runtime type of the object.
* {@link TypeToken} is a class provided by Gson which helps creating parameterized types.
* Here is an example showing how this can be done:
* <pre>
* Type listType = new TypeToken&lt;List&lt;String&gt;&gt;() {}.getType();
* List&lt;String&gt; target = new LinkedList&lt;String&gt;();
* target.add("blah");
* TypeToken&lt;List&lt;MyType&gt;&gt; listType = new TypeToken&lt;List&lt;MyType&gt;&gt;() {};
* List&lt;MyType&gt; target = new LinkedList&lt;MyType&gt;();
* target.add(new MyType(1, "abc"));
*
* Gson gson = new Gson();
* String json = gson.toJson(target, listType);
* List&lt;String&gt; target2 = gson.fromJson(json, listType);
* // For serialization you normally do not have to specify the type, Gson will use
* // the runtime type of the objects, however you can also specify it explicitly
* String json = gson.toJson(target, listType.getType());
*
* // But for deserialization you have to specify the type
* List&lt;MyType&gt; target2 = gson.fromJson(json, listType);
* </pre>
*
* <p>See the <a href="https://sites.google.com/site/gson/gson-user-guide">Gson User Guide</a>
* <p>See the <a href="https://github.com/google/gson/blob/master/UserGuide.md">Gson User Guide</a>
* for a more complete set of examples.</p>
*
* <h2>Lenient JSON handling</h2>
@ -125,7 +131,7 @@ import java.util.concurrent.atomic.AtomicLongArray;
* to make sure there is no trailing data
* </ol>
*
* @see com.google.gson.reflect.TypeToken
* @see TypeToken
*
* @author Inderjeet Singh
* @author Joel Leitch
@ -210,9 +216,9 @@ public final class Gson {
* through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}. </li>
* <li>By default, Gson ignores the {@link com.google.gson.annotations.Since} annotation. You
* can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.</li>
* <li>The default field naming policy for the output Json is same as in Java. So, a Java class
* <li>The default field naming policy for the output JSON is same as in Java. So, a Java class
* field <code>versionNumber</code> will be output as <code>&quot;versionNumber&quot;</code> in
* Json. The same rules are applied for mapping incoming Json to the Java classes. You can
* JSON. The same rules are applied for mapping incoming JSON to the Java classes. You can
* change this policy through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.</li>
* <li>By default, Gson excludes <code>transient</code> or <code>static</code> fields from
* consideration for serialization and deserialization. You can change this behavior through
@ -644,13 +650,15 @@ public final class Gson {
* {@link JsonElement}s. This method should be used when the specified object is not a generic
* type. This method uses {@link Class#getClass()} to get the type for the specified object, but
* the {@code getClass()} loses the generic type information because of the Type Erasure feature
* of Java. Note that this method works fine if the any of the object fields are of generic type,
* of Java. Note that this method works fine if any of the object fields are of generic type,
* just the object itself should not be of a generic type. If the object is of generic type, use
* {@link #toJsonTree(Object, Type)} instead.
*
* @param src the object for which Json representation is to be created setting for Gson
* @return Json representation of {@code src}.
* @param src the object for which JSON representation is to be created
* @return JSON representation of {@code src}.
* @since 1.4
*
* @see #toJsonTree(Object, Type)
*/
public JsonElement toJsonTree(Object src) {
if (src == null) {
@ -674,6 +682,8 @@ public final class Gson {
* </pre>
* @return Json representation of {@code src}
* @since 1.4
*
* @see #toJsonTree(Object)
*/
public JsonElement toJsonTree(Object src, Type typeOfSrc) {
JsonTreeWriter writer = new JsonTreeWriter();
@ -682,17 +692,20 @@ public final class Gson {
}
/**
* This method serializes the specified object into its equivalent Json representation.
* This method serializes the specified object into its equivalent JSON representation.
* This method should be used when the specified object is not a generic type. This method uses
* {@link Class#getClass()} to get the type for the specified object, but the
* {@code getClass()} loses the generic type information because of the Type Erasure feature
* of Java. Note that this method works fine if the any of the object fields are of generic type,
* of Java. Note that this method works fine if any of the object fields are of generic type,
* just the object itself should not be of a generic type. If the object is of generic type, use
* {@link #toJson(Object, Type)} instead. If you want to write out the object to a
* {@link Writer}, use {@link #toJson(Object, Appendable)} instead.
*
* @param src the object for which Json representation is to be created setting for Gson
* @param src the object for which JSON representation is to be created
* @return Json representation of {@code src}.
*
* @see #toJson(Object, Appendable)
* @see #toJson(Object, Type)
*/
public String toJson(Object src) {
if (src == null) {
@ -703,7 +716,7 @@ public final class Gson {
/**
* This method serializes the specified object, including those of generic types, into its
* equivalent Json representation. This method must be used if the specified object is a generic
* equivalent JSON representation. This method must be used if the specified object is a generic
* type. For non-generic objects, use {@link #toJson(Object)} instead. If you want to write out
* the object to a {@link Appendable}, use {@link #toJson(Object, Type, Appendable)} instead.
*
@ -714,7 +727,10 @@ public final class Gson {
* <pre>
* Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* </pre>
* @return Json representation of {@code src}
* @return JSON representation of {@code src}
*
* @see #toJson(Object, Type, Appendable)
* @see #toJson(Object)
*/
public String toJson(Object src, Type typeOfSrc) {
StringWriter writer = new StringWriter();
@ -723,18 +739,22 @@ public final class Gson {
}
/**
* This method serializes the specified object into its equivalent Json representation.
* This method serializes the specified object into its equivalent JSON representation and
* writes it to the writer.
* This method should be used when the specified object is not a generic type. This method uses
* {@link Class#getClass()} to get the type for the specified object, but the
* {@code getClass()} loses the generic type information because of the Type Erasure feature
* of Java. Note that this method works fine if the any of the object fields are of generic type,
* of Java. Note that this method works fine if any of the object fields are of generic type,
* just the object itself should not be of a generic type. If the object is of generic type, use
* {@link #toJson(Object, Type, Appendable)} instead.
*
* @param src the object for which Json representation is to be created setting for Gson
* @param writer Writer to which the Json representation needs to be written
* @param src the object for which JSON representation is to be created
* @param writer Writer to which the JSON representation needs to be written
* @throws JsonIOException if there was a problem writing to the writer
* @since 1.2
*
* @see #toJson(Object)
* @see #toJson(Object, Type, Appendable)
*/
public void toJson(Object src, Appendable writer) throws JsonIOException {
if (src != null) {
@ -746,8 +766,9 @@ public final class Gson {
/**
* This method serializes the specified object, including those of generic types, into its
* equivalent Json representation. This method must be used if the specified object is a generic
* type. For non-generic objects, use {@link #toJson(Object, Appendable)} instead.
* equivalent JSON representation and writes it to the writer.
* This method must be used if the specified object is a generic type. For non-generic objects,
* use {@link #toJson(Object, Appendable)} instead.
*
* @param src the object for which JSON representation is to be created
* @param typeOfSrc The specific genericized type of src. You can obtain
@ -756,9 +777,12 @@ public final class Gson {
* <pre>
* Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* </pre>
* @param writer Writer to which the Json representation of src needs to be written.
* @param writer Writer to which the JSON representation of src needs to be written.
* @throws JsonIOException if there was a problem writing to the writer
* @since 1.2
*
* @see #toJson(Object, Type)
* @see #toJson(Object, Appendable)
*/
public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException {
try {
@ -824,7 +848,7 @@ public final class Gson {
* Writes out the equivalent JSON for a tree of {@link JsonElement}s.
*
* @param jsonElement root of a tree of {@link JsonElement}s
* @param writer Writer to which the Json representation needs to be written
* @param writer Writer to which the JSON representation needs to be written
* @throws JsonIOException if there was a problem writing to the writer
* @since 1.4
*/
@ -913,17 +937,17 @@ public final class Gson {
}
/**
* This method deserializes the specified Json into an object of the specified class. It is not
* 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
* 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, Type)}. If you have the Json in a {@link Reader} instead of
* {@link #fromJson(String, TypeToken)}. If you have the JSON in a {@link Reader} instead of
* a String, use {@link #fromJson(Reader, Class)} instead.
*
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements,
* or if there is trailing data.
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements, or if there
* is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired.
*
* @param <T> the type of the desired object
* @param json the string from which the object is to be deserialized
@ -932,98 +956,165 @@ public final class Gson {
* or if {@code json} is empty.
* @throws JsonSyntaxException if json is not a valid representation for an object of type
* classOfT
*
* @see #fromJson(Reader, Class)
* @see #fromJson(String, TypeToken)
*/
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
Object object = fromJson(json, (Type) classOfT);
T object = fromJson(json, TypeToken.get(classOfT));
return Primitives.wrap(classOfT).cast(object);
}
/**
* This method deserializes the specified Json into an object of the specified type. This method
* This method deserializes the specified JSON into an object of the specified type. This method
* is useful if the specified object is a generic type. For non-generic objects, use
* {@link #fromJson(String, Class)} instead. If you have the Json in a {@link Reader} instead of
* {@link #fromJson(String, Class)} instead. If you have the JSON in a {@link Reader} instead of
* a String, use {@link #fromJson(Reader, Type)} instead.
*
* <p>Since {@code Type} is not parameterized by T, this method is not type-safe and
* should be used carefully. If you are creating the {@code Type} from a {@link TypeToken},
* prefer using {@link #fromJson(String, TypeToken)} instead since its return type is based
* on the {@code TypeToken} and is therefore more type-safe.
*
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements,
* or if there is trailing data.
* or if there is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is
* not desired.
*
* @param <T> the type of the desired object
* @param json the string from which the object is to be deserialized
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
* {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
* @param typeOfT The specific genericized type of src
* @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}
* or if {@code json} is empty.
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
*
* @see #fromJson(Reader, Type)
* @see #fromJson(String, Class)
* @see #fromJson(String, TypeToken)
*/
@SuppressWarnings("unchecked")
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
return (T) fromJson(json, TypeToken.get(typeOfT));
}
/**
* This method deserializes the specified JSON into an object of the specified type. This method
* is useful if the specified object is a generic type. For non-generic objects, use
* {@link #fromJson(String, Class)} instead. If you have the JSON in a {@link Reader} instead of
* a String, use {@link #fromJson(Reader, TypeToken)} instead.
*
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements, or if there
* is trailing data. Use {@link #fromJson(JsonReader, TypeToken)} if this behavior is not desired.
*
* @param <T> the type of the desired object
* @param json the string from which the object is to be deserialized
* @param typeOfT The specific genericized type of src. You should create an anonymous subclass of
* {@code TypeToken} with the specific generic type arguments. For example, to get the type for
* {@code Collection<Foo>}, you should use:
* <pre>
* Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}
* </pre>
* @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}
* or if {@code json} is empty.
* @throws JsonParseException if json is not a valid representation for an object of type typeOfT
* @throws JsonSyntaxException if json is not a valid representation for an object of type
* @throws JsonSyntaxException if json is not a valid representation for an object of the type typeOfT
*
* @see #fromJson(Reader, TypeToken)
* @see #fromJson(String, Class)
*/
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
public <T> T fromJson(String json, TypeToken<T> typeOfT) throws JsonSyntaxException {
if (json == null) {
return null;
}
StringReader reader = new StringReader(json);
@SuppressWarnings("unchecked")
T target = (T) fromJson(reader, typeOfT);
return target;
return fromJson(reader, typeOfT);
}
/**
* This method deserializes the Json read from the specified reader into an object of the
* This method deserializes the JSON read from the specified reader 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 the fields of the specified object are generics, just the
* 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(Reader, Type)}. If you have the Json in a String form instead of a
* invoke {@link #fromJson(Reader, TypeToken)}. If you have the JSON in a String form instead of a
* {@link Reader}, use {@link #fromJson(String, Class)} instead.
*
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements,
* or if there is trailing data.
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements, or if there
* is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired.
*
* @param <T> the type of the desired object
* @param json the reader producing the Json from which the object is to be deserialized.
* @param json the reader producing the JSON from which the object is to be deserialized.
* @param classOfT the class of T
* @return an object of type T from the string. Returns {@code null} if {@code json} is at EOF.
* @return an object of type T from the Reader. Returns {@code null} if {@code json} is at EOF.
* @throws JsonIOException if there was a problem reading from the Reader
* @throws JsonSyntaxException if json is not a valid representation for an object of type
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
* @since 1.2
*
* @see #fromJson(String, Class)
* @see #fromJson(Reader, TypeToken)
*/
public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException, JsonIOException {
JsonReader jsonReader = newJsonReader(json);
Object object = fromJson(jsonReader, classOfT);
assertFullConsumption(object, jsonReader);
T object = fromJson(json, TypeToken.get(classOfT));
return Primitives.wrap(classOfT).cast(object);
}
/**
* This method deserializes the Json read from the specified reader into an object of the
* This method deserializes the JSON read from the specified reader into an object of the
* specified type. This method is useful if the specified object is a generic type. For
* non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the Json in a
* non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the JSON in a
* String form instead of a {@link Reader}, use {@link #fromJson(String, Type)} instead.
*
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements,
* or if there is trailing data.
* <p>Since {@code Type} is not parameterized by T, this method is not type-safe and
* should be used carefully. If you are creating the {@code Type} from a {@link TypeToken},
* prefer using {@link #fromJson(Reader, TypeToken)} instead since its return type is based
* on the {@code TypeToken} and is therefore more type-safe.
*
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements, or if there
* is trailing data. Use {@link #fromJson(JsonReader, Type)} if this behavior is not desired.
*
* @param <T> the type of the desired object
* @param json the reader producing Json from which the object is to be deserialized
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
* {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
* @param json the reader producing JSON from which the object is to be deserialized
* @param typeOfT The specific genericized type of src
* @return an object of type T from the Reader. Returns {@code null} if {@code json} is at EOF.
* @throws JsonIOException if there was a problem reading from the Reader
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
* @since 1.2
*
* @see #fromJson(String, Type)
* @see #fromJson(Reader, Class)
* @see #fromJson(Reader, TypeToken)
*/
@SuppressWarnings("unchecked")
public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
return (T) fromJson(json, TypeToken.get(typeOfT));
}
/**
* This method deserializes the JSON read from the specified reader into an object of the
* specified type. This method is useful if the specified object is a generic type. For
* non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the JSON in a
* String form instead of a {@link Reader}, use {@link #fromJson(String, TypeToken)} instead.
*
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements, or if there
* is trailing data. Use {@link #fromJson(JsonReader, TypeToken)} if this behavior is not desired.
*
* @param <T> the type of the desired object
* @param json the reader producing JSON from which the object is to be deserialized
* @param typeOfT The specific genericized type of src. You should create an anonymous subclass of
* {@code TypeToken} with the specific generic type arguments. For example, to get the type for
* {@code Collection<Foo>}, you should use:
* <pre>
* Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}
* </pre>
* @return an object of type T from the json. Returns {@code null} if {@code json} is at EOF.
* @return an object of type T from the Reader. Returns {@code null} if {@code json} is at EOF.
* @throws JsonIOException if there was a problem reading from the Reader
* @throws JsonSyntaxException if json is not a valid representation for an object of type
* @since 1.2
* @throws JsonSyntaxException if json is not a valid representation for an object of type of typeOfT
*
* @see #fromJson(String, TypeToken)
* @see #fromJson(Reader, Class)
*/
public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
public <T> T fromJson(Reader json, TypeToken<T> typeOfT) throws JsonIOException, JsonSyntaxException {
JsonReader jsonReader = newJsonReader(json);
@SuppressWarnings("unchecked")
T object = (T) fromJson(jsonReader, typeOfT);
T object = fromJson(jsonReader, typeOfT);
assertFullConsumption(object, jsonReader);
return object;
}
@ -1040,10 +1131,18 @@ public final class Gson {
}
}
// fromJson(JsonReader, Class) is unfortunately missing and cannot be added now without breaking
// source compatibility in certain cases, see https://github.com/google/gson/pull/1700#discussion_r973764414
/**
* Reads the next JSON value from {@code reader} and convert it to an object
* Reads the next JSON value from {@code reader} and converts it to an object
* of type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF.
* Since Type is not parameterized by T, this method is type unsafe and should be used carefully.
*
* <p>Since {@code Type} is not parameterized by T, this method is not type-safe and
* should be used carefully. If you are creating the {@code Type} from a {@link TypeToken},
* prefer using {@link #fromJson(JsonReader, TypeToken)} instead since its return type is based
* on the {@code TypeToken} and is therefore more type-safe. If the provided type is a
* {@code Class} the {@code TypeToken} can be created with {@link TypeToken#get(Class)}.
*
* <p>Unlike the other {@code fromJson} methods, no exception is thrown if the JSON data has
* multiple top-level JSON elements, or if there is trailing data.
@ -1052,19 +1151,58 @@ public final class Gson {
* regardless of the lenient mode setting of the provided reader. The lenient mode setting
* of the reader is restored once this method returns.
*
* @throws JsonIOException if there was a problem writing to the Reader
* @throws JsonSyntaxException if json is not a valid representation for an object of type
* @param <T> the type of the desired object
* @param reader the reader whose next JSON value should be deserialized
* @param typeOfT The specific genericized type of src
* @return an object of type T from the JsonReader. Returns {@code null} if {@code reader} is at EOF.
* @throws JsonIOException if there was a problem reading from the JsonReader
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
*
* @see #fromJson(Reader, Type)
* @see #fromJson(JsonReader, TypeToken)
*/
@SuppressWarnings("unchecked")
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
return (T) fromJson(reader, TypeToken.get(typeOfT));
}
/**
* Reads the next JSON value from {@code reader} and converts it to an object
* of type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF.
* This method is useful if the specified object is a generic type. For non-generic objects,
* {@link #fromJson(JsonReader, Type)} can be called, or {@link TypeToken#get(Class)} can
* be used to create the type token.
*
* <p>Unlike the other {@code fromJson} methods, no exception is thrown if the JSON data has
* multiple top-level JSON elements, or if there is trailing data.
*
* <p>The JSON data is parsed in {@linkplain JsonReader#setLenient(boolean) lenient mode},
* regardless of the lenient mode setting of the provided reader. The lenient mode setting
* of the reader is restored once this method returns.
*
* @param <T> the type of the desired object
* @param reader the reader whose next JSON value should be deserialized
* @param typeOfT The specific genericized type of src. You should create an anonymous subclass of
* {@code TypeToken} with the specific generic type arguments. For example, to get the type for
* {@code Collection<Foo>}, you should use:
* <pre>
* new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}
* </pre>
* @return an object of type T from the JsonReader. Returns {@code null} if {@code reader} is at EOF.
* @throws JsonIOException if there was a problem reading from the JsonReader
* @throws JsonSyntaxException if json is not a valid representation for an object of the type typeOfT
*
* @see #fromJson(Reader, TypeToken)
* @see #fromJson(JsonReader, Type)
*/
public <T> T fromJson(JsonReader reader, TypeToken<T> typeOfT) throws JsonIOException, JsonSyntaxException {
boolean isEmpty = true;
boolean oldLenient = reader.isLenient();
reader.setLenient(true);
try {
reader.peek();
isEmpty = false;
@SuppressWarnings("unchecked")
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
TypeAdapter<T> typeAdapter = getAdapter(typeOfT);
T object = typeAdapter.read(reader);
return object;
} catch (EOFException e) {
@ -1091,52 +1229,86 @@ public final class Gson {
}
/**
* This method deserializes the Json read from the specified parse tree into an object of the
* This method deserializes the JSON read from the specified parse tree into an object of the
* specified type. 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 the fields of the specified object are generics, just the
* 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(JsonElement, Type)}.
* invoke {@link #fromJson(JsonElement, TypeToken)}.
*
* @param <T> the type of the desired object
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to
* be deserialized
* @param classOfT The class of T
* @return an object of type T from the json. Returns {@code null} if {@code json} is {@code null}
* @return an object of type T from the JSON. Returns {@code null} if {@code json} is {@code null}
* or if {@code json} is empty.
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
* @throws JsonSyntaxException if json is not a valid representation for an object of type classOfT
* @since 1.3
*
* @see #fromJson(Reader, Class)
* @see #fromJson(JsonElement, TypeToken)
*/
public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException {
Object object = fromJson(json, (Type) classOfT);
T object = fromJson(json, TypeToken.get(classOfT));
return Primitives.wrap(classOfT).cast(object);
}
/**
* This method deserializes the Json read from the specified parse tree into an object of the
* This method deserializes the JSON read from the specified parse tree into an object of the
* specified type. This method is useful if the specified object is a generic type. For
* non-generic objects, use {@link #fromJson(JsonElement, Class)} instead.
*
* <p>Since {@code Type} is not parameterized by T, this method is not type-safe and
* should be used carefully. If you are creating the {@code Type} from a {@link TypeToken},
* prefer using {@link #fromJson(JsonElement, TypeToken)} instead since its return type is based
* on the {@code TypeToken} and is therefore more type-safe.
*
* @param <T> the type of the desired object
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to
* be deserialized
* @param typeOfT The specific genericized type of src
* @return an object of type T from the JSON. Returns {@code null} if {@code json} is {@code null}
* or if {@code json} is empty.
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
* @since 1.3
*
* @see #fromJson(Reader, Type)
* @see #fromJson(JsonElement, Class)
* @see #fromJson(JsonElement, TypeToken)
*/
@SuppressWarnings("unchecked")
public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
return (T) fromJson(json, TypeToken.get(typeOfT));
}
/**
* This method deserializes the JSON read from the specified parse tree into an object of the
* specified type. This method is useful if the specified object is a generic type. For
* non-generic objects, use {@link #fromJson(JsonElement, Class)} instead.
*
* @param <T> the type of the desired object
* @param json the root of the parse tree of {@link JsonElement}s from which the object is to
* be deserialized
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
* {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
* @param typeOfT The specific genericized type of src. You should create an anonymous subclass of
* {@code TypeToken} with the specific generic type arguments. For example, to get the type for
* {@code Collection<Foo>}, you should use:
* <pre>
* Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}
* </pre>
* @return an object of type T from the json. Returns {@code null} if {@code json} is {@code null}
* @return an object of type T from the JSON. Returns {@code null} if {@code json} is {@code null}
* or if {@code json} is empty.
* @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
* @since 1.3
*
* @see #fromJson(Reader, TypeToken)
* @see #fromJson(JsonElement, Class)
*/
@SuppressWarnings("unchecked")
public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
public <T> T fromJson(JsonElement json, TypeToken<T> typeOfT) throws JsonSyntaxException {
if (json == null) {
return null;
}
return (T) fromJson(new JsonTreeReader(json), typeOfT);
return fromJson(new JsonTreeReader(json), typeOfT);
}
static class FutureTypeAdapter<T> extends TypeAdapter<T> {

View File

@ -32,7 +32,7 @@ import java.util.Objects;
* runtime.
*
* <p>For example, to create a type literal for {@code List<String>}, you can
* create an empty anonymous inner class:
* create an empty anonymous class:
*
* <p>
* {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};}
@ -43,6 +43,11 @@ import java.util.Objects;
* might expect, which gives a false sense of type-safety at compilation time
* and can lead to an unexpected {@code ClassCastException} at runtime.
*
* <p>If the type arguments of the parameterized type are only available at
* runtime, for example when you want to create a {@code List<E>} based on
* a {@code Class<E>} representing the element type, the method
* {@link #getParameterized(Type, Type...)} can be used.
*
* @author Bob Lee
* @author Sven Mawson
* @author Jesse Wilson
@ -317,8 +322,17 @@ public class TypeToken<T> {
}
/**
* Gets type literal for the parameterized type represented by applying {@code typeArguments} to
* {@code rawType}.
* Gets a type literal for the parameterized type represented by applying {@code typeArguments} to
* {@code rawType}. This is mainly intended for situations where the type arguments are not
* available at compile time. The following example shows how a type token for {@code Map<K, V>}
* can be created:
* <pre>{@code
* Class<K> keyClass = ...;
* Class<V> valueClass = ...;
* TypeToken<?> mapTypeToken = TypeToken.getParameterized(Map.class, keyClass, valueClass);
* }</pre>
* As seen here the result is a {@code TypeToken<?>}; this method cannot provide any type safety,
* and care must be taken to pass in the correct number of type arguments.
*
* @throws IllegalArgumentException
* If {@code rawType} is not of type {@code Class}, or if the type arguments are invalid for

View File

@ -174,7 +174,7 @@ public final class MixedStreamTest extends TestCase {
} catch (NullPointerException expected) {
}
try {
gson.fromJson(new JsonReader(new StringReader("true")), null);
gson.fromJson(new JsonReader(new StringReader("true")), (Type) null);
fail();
} catch (NullPointerException expected) {
}

View File

@ -16,13 +16,19 @@
package com.google.gson.functional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.ParameterizedTypeFixtures.MyParameterizedType;
import com.google.gson.ParameterizedTypeFixtures.MyParameterizedTypeAdapter;
import com.google.gson.ParameterizedTypeFixtures.MyParameterizedTypeInstanceCreator;
import com.google.gson.common.TestTypes.BagOfPrimitives;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
@ -32,7 +38,8 @@ import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import junit.framework.TestCase;
import org.junit.Before;
import org.junit.Test;
/**
* Functional tests for the serialization and deserialization of parameterized types in Gson.
@ -40,15 +47,15 @@ import junit.framework.TestCase;
* @author Inderjeet Singh
* @author Joel Leitch
*/
public class ParameterizedTypesTest extends TestCase {
public class ParameterizedTypesTest {
private Gson gson;
@Override
protected void setUp() throws Exception {
super.setUp();
@Before
public void setUp() {
gson = new Gson();
}
@Test
public void testParameterizedTypesSerialization() throws Exception {
MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
Type typeOfSrc = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
@ -56,6 +63,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(src.getExpectedJson(), json);
}
@Test
public void testParameterizedTypeDeserialization() throws Exception {
BagOfPrimitives bag = new BagOfPrimitives();
MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<>(bag);
@ -70,6 +78,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(expected, actual);
}
@Test
public void testTypesWithMultipleParametersSerialization() throws Exception {
MultiParameters<Integer, Float, Double, String, BagOfPrimitives> src =
new MultiParameters<>(10, 1.0F, 2.1D, "abc", new BagOfPrimitives());
@ -81,6 +90,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(expected, json);
}
@Test
public void testTypesWithMultipleParametersDeserialization() throws Exception {
Type typeOfTarget = new TypeToken<MultiParameters<Integer, Float, Double, String,
BagOfPrimitives>>() {}.getType();
@ -93,6 +103,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(expected, target);
}
@Test
public void testParameterizedTypeWithCustomSerializer() {
Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
@ -109,6 +120,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(MyParameterizedTypeAdapter.<String>getExpectedJson(stringTarget), json);
}
@Test
public void testParameterizedTypesWithCustomDeserializer() {
Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
@ -130,6 +142,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals("abc", stringTarget.value);
}
@Test
public void testParameterizedTypesWithWriterSerialization() throws Exception {
Writer writer = new StringWriter();
MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
@ -138,6 +151,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(src.getExpectedJson(), writer.toString());
}
@Test
public void testParameterizedTypeWithReaderDeserialization() throws Exception {
BagOfPrimitives bag = new BagOfPrimitives();
MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<>(bag);
@ -158,6 +172,7 @@ public class ParameterizedTypesTest extends TestCase {
return args;
}
@Test
public void testVariableTypeFieldsAndGenericArraysSerialization() throws Exception {
Integer obj = 0;
Integer[] array = { 1, 2, 3 };
@ -174,6 +189,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(objToSerialize.getExpectedJson(), json);
}
@Test
public void testVariableTypeFieldsAndGenericArraysDeserialization() throws Exception {
Integer obj = 0;
Integer[] array = { 1, 2, 3 };
@ -191,6 +207,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(objAfterDeserialization.getExpectedJson(), json);
}
@Test
public void testVariableTypeDeserialization() throws Exception {
Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
ObjectWithTypeVariables<Integer> objToSerialize =
@ -201,6 +218,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(objAfterDeserialization.getExpectedJson(), json);
}
@Test
public void testVariableTypeArrayDeserialization() throws Exception {
Integer[] array = { 1, 2, 3 };
@ -213,6 +231,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(objAfterDeserialization.getExpectedJson(), json);
}
@Test
public void testParameterizedTypeWithVariableTypeDeserialization() throws Exception {
List<Integer> list = new ArrayList<>();
list.add(4);
@ -227,6 +246,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(objAfterDeserialization.getExpectedJson(), json);
}
@Test
public void testParameterizedTypeGenericArraysSerialization() throws Exception {
List<Integer> list = new ArrayList<>();
list.add(1);
@ -240,6 +260,7 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals("{\"arrayOfListOfTypeParameters\":[[1,2],[1,2]]}", json);
}
@Test
public void testParameterizedTypeGenericArraysDeserialization() throws Exception {
List<Integer> list = new ArrayList<>();
list.add(1);
@ -483,6 +504,7 @@ public class ParameterizedTypesTest extends TestCase {
int value = 30;
}
@Test
public void testDeepParameterizedTypeSerialization() {
Amount<MyQuantity> amount = new Amount<>();
String json = gson.toJson(amount);
@ -490,6 +512,7 @@ public class ParameterizedTypesTest extends TestCase {
assertTrue(json.contains("30"));
}
@Test
public void testDeepParameterizedTypeDeserialization() {
String json = "{value:30}";
Type type = new TypeToken<Amount<MyQuantity>>() {}.getType();
@ -497,4 +520,47 @@ public class ParameterizedTypesTest extends TestCase {
assertEquals(30, amount.value);
}
// End: tests to reproduce issue 103
private static void assertCorrectlyDeserialized(Object object) {
@SuppressWarnings("unchecked")
List<Quantity> list = (List<Quantity>) object;
assertEquals(1, list.size());
assertEquals(4, list.get(0).q);
}
@Test
public void testGsonFromJsonTypeToken() {
TypeToken<List<Quantity>> typeToken = new TypeToken<List<Quantity>>() {};
Type type = typeToken.getType();
{
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("q", 4);
JsonArray jsonArray = new JsonArray();
jsonArray.add(jsonObject);
assertCorrectlyDeserialized(gson.fromJson(jsonArray, typeToken));
assertCorrectlyDeserialized(gson.fromJson(jsonArray, type));
}
String json = "[{\"q\":4}]";
{
assertCorrectlyDeserialized(gson.fromJson(json, typeToken));
assertCorrectlyDeserialized(gson.fromJson(json, type));
}
{
assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), typeToken));
assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), type));
}
{
JsonReader reader = new JsonReader(new StringReader(json));
assertCorrectlyDeserialized(gson.fromJson(reader, typeToken));
reader = new JsonReader(new StringReader(json));
assertCorrectlyDeserialized(gson.fromJson(reader, type));
}
}
}

View File

@ -33,14 +33,15 @@ import java.util.List;
*/
public class CollectionsDeserializationBenchmark {
private static final Type LIST_TYPE = new TypeToken<List<BagOfPrimitives>>(){}.getType();
private static final TypeToken<List<BagOfPrimitives>> LIST_TYPE_TOKEN = new TypeToken<List<BagOfPrimitives>>(){};
private static final Type LIST_TYPE = LIST_TYPE_TOKEN.getType();
private Gson gson;
private String json;
public static void main(String[] args) {
NonUploadingCaliperRunner.run(CollectionsDeserializationBenchmark.class, args);
}
@BeforeExperiment
void setUp() throws Exception {
this.gson = new Gson();
@ -51,12 +52,12 @@ public class CollectionsDeserializationBenchmark {
this.json = gson.toJson(bags, LIST_TYPE);
}
/**
/**
* Benchmark to measure Gson performance for deserializing an object
*/
public void timeCollectionsDefault(int reps) {
for (int i=0; i<reps; ++i) {
gson.fromJson(json, LIST_TYPE);
gson.fromJson(json, LIST_TYPE_TOKEN);
}
}

View File

@ -63,11 +63,11 @@ public final class ParseBenchmark {
READER_SHORT(new TypeToken<Feed>() {}, new TypeReference<Feed>() {}),
READER_LONG(new TypeToken<Feed>() {}, new TypeReference<Feed>() {});
private final Type gsonType;
private final TypeToken<?> gsonType;
private final TypeReference<?> jacksonType;
private Document(TypeToken<?> typeToken, TypeReference<?> typeReference) {
this.gsonType = typeToken.getType();
this.gsonType = typeToken;
this.jacksonType = typeReference;
}
}