diff --git a/UserGuide.md b/UserGuide.md index 12b53351bd..2aafb067e8 100644 --- a/UserGuide.md +++ b/UserGuide.md @@ -225,7 +225,7 @@ Collection 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>(){}.getType(); +TypeToken> collectionType = new TypeToken>(){}; Collection 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>(){}.getType(); +TypeToken> mapType = new TypeToken>(){}; String json = "{\"key\": \"value\"}"; // Deserialization diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 168bc6f1c4..c3262a6fe0 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -76,26 +76,32 @@ *
  * 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
  * 
* - *

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}: - * + *

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}) 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: *

- * Type listType = new TypeToken<List<String>>() {}.getType();
- * List<String> target = new LinkedList<String>();
- * target.add("blah");
+ * TypeToken<List<MyType>> listType = new TypeToken<List<MyType>>() {};
+ * List<MyType> target = new LinkedList<MyType>();
+ * target.add(new MyType(1, "abc"));
  *
  * Gson gson = new Gson();
- * String json = gson.toJson(target, listType);
- * List<String> 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<MyType> target2 = gson.fromJson(json, listType);
  * 
* - *

See the Gson User Guide + *

See the Gson User Guide * for a more complete set of examples.

* *

Lenient JSON handling

@@ -125,7 +131,7 @@ * to make sure there is no trailing data * * - * @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()}. *
  • 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)}.
  • - *
  • The default field naming policy for the output Json is same as in Java. So, a Java class + *
  • The default field naming policy for the output JSON is same as in Java. So, a Java class * field versionNumber will be output as "versionNumber" 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)}.
  • *
  • By default, Gson excludes transient or static fields from * consideration for serialization and deserialization. You can change this behavior through @@ -644,13 +650,15 @@ public TypeAdapter getAdapter(Class type) { * {@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 JsonElement toJsonTree(Object src) { * * @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 JsonElement toJsonTree(Object src, Type typeOfSrc) { } /** - * 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 String toJson(Object src) { /** * 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 String toJson(Object src) { *
        * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
        * 
    - * @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 String toJson(Object src, Type typeOfSrc) { } /** - * 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 void toJson(Object src, Appendable writer) throws JsonIOException { /** * 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 void toJson(Object src, Appendable writer) throws JsonIOException { *
        * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
        * 
    - * @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 String toJson(JsonElement jsonElement) { * 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 void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOExce } /** - * 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. * - *

    An exception is thrown if the JSON string has multiple top-level JSON elements, - * or if there is trailing data. + *

    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 the type of the desired object * @param json the string from which the object is to be deserialized @@ -932,98 +956,165 @@ public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOExce * 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 fromJson(String json, Class 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. * + *

    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. + * *

    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 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 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. + * + *

    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 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}, you should use: *

    -   * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
    +   * new TypeToken<Collection<Foo>>(){}
        * 
    * @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 fromJson(String json, Type typeOfT) throws JsonSyntaxException { + public T fromJson(String json, TypeToken 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. * - *

    An exception is thrown if the JSON data has multiple top-level JSON elements, - * or if there is trailing data. + *

    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 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 fromJson(Reader json, Class 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. * - *

    An exception is thrown if the JSON data has multiple top-level JSON elements, - * or if there is trailing data. + *

    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. + * + *

    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 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 + * @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 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. + * + *

    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 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. You should create an anonymous subclass of + * {@code TypeToken} with the specific generic type arguments. For example, to get the type for * {@code Collection}, you should use: *

    -   * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
    +   * new TypeToken<Collection<Foo>>(){}
        * 
    - * @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 fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { + public T fromJson(Reader json, TypeToken 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 @@ private static void assertFullConsumption(Object obj, JsonReader reader) { } } + // 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. + * + *

    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)}. * *

    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 @@ private static void assertFullConsumption(Object obj, JsonReader reader) { * 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 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 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. + * + *

    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. + * + *

    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 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}, you should use: + *

    +   * new TypeToken<Collection<Foo>>(){}
    +   * 
    + * @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 fromJson(JsonReader reader, TypeToken typeOfT) throws JsonIOException, JsonSyntaxException { boolean isEmpty = true; boolean oldLenient = reader.isLenient(); reader.setLenient(true); try { reader.peek(); isEmpty = false; - @SuppressWarnings("unchecked") - TypeToken typeToken = (TypeToken) TypeToken.get(typeOfT); - TypeAdapter typeAdapter = getAdapter(typeToken); + TypeAdapter typeAdapter = getAdapter(typeOfT); T object = typeAdapter.read(reader); return object; } catch (EOFException e) { @@ -1091,52 +1229,86 @@ public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, J } /** - * 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 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 fromJson(JsonElement json, Class 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. * + *

    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 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 + * @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 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 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 should create an anonymous subclass of + * {@code TypeToken} with the specific generic type arguments. For example, to get the type for * {@code Collection}, you should use: *

    -   * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
    +   * new TypeToken<Collection<Foo>>(){}
        * 
    - * @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 fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException { + public T fromJson(JsonElement json, TypeToken typeOfT) throws JsonSyntaxException { if (json == null) { return null; } - return (T) fromJson(new JsonTreeReader(json), typeOfT); + return fromJson(new JsonTreeReader(json), typeOfT); } static class FutureTypeAdapter extends TypeAdapter { diff --git a/gson/src/main/java/com/google/gson/reflect/TypeToken.java b/gson/src/main/java/com/google/gson/reflect/TypeToken.java index 547e5f5b5f..39e81f33f9 100644 --- a/gson/src/main/java/com/google/gson/reflect/TypeToken.java +++ b/gson/src/main/java/com/google/gson/reflect/TypeToken.java @@ -32,7 +32,7 @@ * runtime. * *

    For example, to create a type literal for {@code List}, you can - * create an empty anonymous inner class: + * create an empty anonymous class: * *

    * {@code TypeToken> list = new TypeToken>() {};} @@ -43,6 +43,11 @@ * might expect, which gives a false sense of type-safety at compilation time * and can lead to an unexpected {@code ClassCastException} at runtime. * + *

    If the type arguments of the parameterized type are only available at + * runtime, for example when you want to create a {@code List} based on + * a {@code Class} 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 static TypeToken get(Class type) { } /** - * 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} + * can be created: + *

    {@code
    +   * Class keyClass = ...;
    +   * Class valueClass = ...;
    +   * TypeToken mapTypeToken = TypeToken.getParameterized(Map.class, keyClass, valueClass);
    +   * }
    + * 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 diff --git a/gson/src/test/java/com/google/gson/MixedStreamTest.java b/gson/src/test/java/com/google/gson/MixedStreamTest.java index 00eb4bc8a0..fa16659f0d 100644 --- a/gson/src/test/java/com/google/gson/MixedStreamTest.java +++ b/gson/src/test/java/com/google/gson/MixedStreamTest.java @@ -174,7 +174,7 @@ public void testReadNulls() { } 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) { } diff --git a/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java b/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java index 49cb0db9bc..e616874620 100644 --- a/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java +++ b/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java @@ -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.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 @@ * @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 src = new MyParameterizedType<>(10); Type typeOfSrc = new TypeToken>() {}.getType(); @@ -56,6 +63,7 @@ public void testParameterizedTypesSerialization() throws Exception { assertEquals(src.getExpectedJson(), json); } + @Test public void testParameterizedTypeDeserialization() throws Exception { BagOfPrimitives bag = new BagOfPrimitives(); MyParameterizedType expected = new MyParameterizedType<>(bag); @@ -70,6 +78,7 @@ public void testParameterizedTypeDeserialization() throws Exception { assertEquals(expected, actual); } + @Test public void testTypesWithMultipleParametersSerialization() throws Exception { MultiParameters src = new MultiParameters<>(10, 1.0F, 2.1D, "abc", new BagOfPrimitives()); @@ -81,6 +90,7 @@ public void testTypesWithMultipleParametersSerialization() throws Exception { assertEquals(expected, json); } + @Test public void testTypesWithMultipleParametersDeserialization() throws Exception { Type typeOfTarget = new TypeToken>() {}.getType(); @@ -93,6 +103,7 @@ public void testTypesWithMultipleParametersDeserialization() throws Exception { assertEquals(expected, target); } + @Test public void testParameterizedTypeWithCustomSerializer() { Type ptIntegerType = new TypeToken>() {}.getType(); Type ptStringType = new TypeToken>() {}.getType(); @@ -109,6 +120,7 @@ public void testParameterizedTypeWithCustomSerializer() { assertEquals(MyParameterizedTypeAdapter.getExpectedJson(stringTarget), json); } + @Test public void testParameterizedTypesWithCustomDeserializer() { Type ptIntegerType = new TypeToken>() {}.getType(); Type ptStringType = new TypeToken>() {}.getType(); @@ -130,6 +142,7 @@ public void testParameterizedTypesWithCustomDeserializer() { assertEquals("abc", stringTarget.value); } + @Test public void testParameterizedTypesWithWriterSerialization() throws Exception { Writer writer = new StringWriter(); MyParameterizedType src = new MyParameterizedType<>(10); @@ -138,6 +151,7 @@ public void testParameterizedTypesWithWriterSerialization() throws Exception { assertEquals(src.getExpectedJson(), writer.toString()); } + @Test public void testParameterizedTypeWithReaderDeserialization() throws Exception { BagOfPrimitives bag = new BagOfPrimitives(); MyParameterizedType expected = new MyParameterizedType<>(bag); @@ -158,6 +172,7 @@ private static T[] arrayOf(T... args) { return args; } + @Test public void testVariableTypeFieldsAndGenericArraysSerialization() throws Exception { Integer obj = 0; Integer[] array = { 1, 2, 3 }; @@ -174,6 +189,7 @@ public void testVariableTypeFieldsAndGenericArraysSerialization() throws Excepti assertEquals(objToSerialize.getExpectedJson(), json); } + @Test public void testVariableTypeFieldsAndGenericArraysDeserialization() throws Exception { Integer obj = 0; Integer[] array = { 1, 2, 3 }; @@ -191,6 +207,7 @@ public void testVariableTypeFieldsAndGenericArraysDeserialization() throws Excep assertEquals(objAfterDeserialization.getExpectedJson(), json); } + @Test public void testVariableTypeDeserialization() throws Exception { Type typeOfSrc = new TypeToken>() {}.getType(); ObjectWithTypeVariables objToSerialize = @@ -201,6 +218,7 @@ public void testVariableTypeDeserialization() throws Exception { assertEquals(objAfterDeserialization.getExpectedJson(), json); } + @Test public void testVariableTypeArrayDeserialization() throws Exception { Integer[] array = { 1, 2, 3 }; @@ -213,6 +231,7 @@ public void testVariableTypeArrayDeserialization() throws Exception { assertEquals(objAfterDeserialization.getExpectedJson(), json); } + @Test public void testParameterizedTypeWithVariableTypeDeserialization() throws Exception { List list = new ArrayList<>(); list.add(4); @@ -227,6 +246,7 @@ public void testParameterizedTypeWithVariableTypeDeserialization() throws Except assertEquals(objAfterDeserialization.getExpectedJson(), json); } + @Test public void testParameterizedTypeGenericArraysSerialization() throws Exception { List list = new ArrayList<>(); list.add(1); @@ -240,6 +260,7 @@ public void testParameterizedTypeGenericArraysSerialization() throws Exception { assertEquals("{\"arrayOfListOfTypeParameters\":[[1,2],[1,2]]}", json); } + @Test public void testParameterizedTypeGenericArraysDeserialization() throws Exception { List list = new ArrayList<>(); list.add(1); @@ -483,6 +504,7 @@ public static final class Amount int value = 30; } + @Test public void testDeepParameterizedTypeSerialization() { Amount amount = new Amount<>(); String json = gson.toJson(amount); @@ -490,6 +512,7 @@ public void testDeepParameterizedTypeSerialization() { assertTrue(json.contains("30")); } + @Test public void testDeepParameterizedTypeDeserialization() { String json = "{value:30}"; Type type = new TypeToken>() {}.getType(); @@ -497,4 +520,47 @@ public void testDeepParameterizedTypeDeserialization() { assertEquals(30, amount.value); } // End: tests to reproduce issue 103 + + private static void assertCorrectlyDeserialized(Object object) { + @SuppressWarnings("unchecked") + List list = (List) object; + assertEquals(1, list.size()); + assertEquals(4, list.get(0).q); + } + + @Test + public void testGsonFromJsonTypeToken() { + TypeToken> typeToken = new TypeToken>() {}; + 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)); + } + } } diff --git a/metrics/src/main/java/com/google/gson/metrics/CollectionsDeserializationBenchmark.java b/metrics/src/main/java/com/google/gson/metrics/CollectionsDeserializationBenchmark.java index dad0d99ae7..738b5ae4bf 100644 --- a/metrics/src/main/java/com/google/gson/metrics/CollectionsDeserializationBenchmark.java +++ b/metrics/src/main/java/com/google/gson/metrics/CollectionsDeserializationBenchmark.java @@ -33,14 +33,15 @@ */ public class CollectionsDeserializationBenchmark { - private static final Type LIST_TYPE = new TypeToken>(){}.getType(); + private static final TypeToken> LIST_TYPE_TOKEN = new TypeToken>(){}; + 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 @@ void setUp() throws Exception { 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() {}, new TypeReference() {}), READER_LONG(new TypeToken() {}, new TypeReference() {}); - 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; } }