Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 12 commits into from
Sep 19, 2022
Merged
4 changes: 2 additions & 2 deletions UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down Expand Up @@ -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
Expand Down
360 changes: 266 additions & 94 deletions gson/src/main/java/com/google/gson/Gson.java

Large diffs are not rendered by default.

20 changes: 17 additions & 3 deletions gson/src/main/java/com/google/gson/reflect/TypeToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* 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>>() {};}
Expand All @@ -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.
*
* <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
Expand Down Expand Up @@ -317,8 +322,17 @@ public static <T> TypeToken<T> get(Class<T> 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<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
Expand Down
2 changes: 1 addition & 1 deletion gson/src/test/java/com/google/gson/MixedStreamTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,30 +38,32 @@
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.
*
* @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();
String json = gson.toJson(src, typeOfSrc);
assertEquals(src.getExpectedJson(), json);
}

@Test
public void testParameterizedTypeDeserialization() throws Exception {
BagOfPrimitives bag = new BagOfPrimitives();
MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<>(bag);
Expand All @@ -70,6 +78,7 @@ public void testParameterizedTypeDeserialization() throws Exception {
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());
Expand All @@ -81,6 +90,7 @@ public void testTypesWithMultipleParametersSerialization() throws Exception {
assertEquals(expected, json);
}

@Test
public void testTypesWithMultipleParametersDeserialization() throws Exception {
Type typeOfTarget = new TypeToken<MultiParameters<Integer, Float, Double, String,
BagOfPrimitives>>() {}.getType();
Expand All @@ -93,6 +103,7 @@ public void testTypesWithMultipleParametersDeserialization() throws Exception {
assertEquals(expected, target);
}

@Test
public void testParameterizedTypeWithCustomSerializer() {
Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
Expand All @@ -109,6 +120,7 @@ public void testParameterizedTypeWithCustomSerializer() {
assertEquals(MyParameterizedTypeAdapter.<String>getExpectedJson(stringTarget), json);
}

@Test
public void testParameterizedTypesWithCustomDeserializer() {
Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
Expand All @@ -130,6 +142,7 @@ public void testParameterizedTypesWithCustomDeserializer() {
assertEquals("abc", stringTarget.value);
}

@Test
public void testParameterizedTypesWithWriterSerialization() throws Exception {
Writer writer = new StringWriter();
MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
Expand All @@ -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<BagOfPrimitives> expected = new MyParameterizedType<>(bag);
Expand All @@ -158,6 +172,7 @@ private static <T> T[] arrayOf(T... args) {
return args;
}

@Test
public void testVariableTypeFieldsAndGenericArraysSerialization() throws Exception {
Integer obj = 0;
Integer[] array = { 1, 2, 3 };
Expand All @@ -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 };
Expand All @@ -191,6 +207,7 @@ public void testVariableTypeFieldsAndGenericArraysDeserialization() throws Excep
assertEquals(objAfterDeserialization.getExpectedJson(), json);
}

@Test
public void testVariableTypeDeserialization() throws Exception {
Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
ObjectWithTypeVariables<Integer> objToSerialize =
Expand All @@ -201,6 +218,7 @@ public void testVariableTypeDeserialization() throws Exception {
assertEquals(objAfterDeserialization.getExpectedJson(), json);
}

@Test
public void testVariableTypeArrayDeserialization() throws Exception {
Integer[] array = { 1, 2, 3 };

Expand All @@ -213,6 +231,7 @@ public void testVariableTypeArrayDeserialization() throws Exception {
assertEquals(objAfterDeserialization.getExpectedJson(), json);
}

@Test
public void testParameterizedTypeWithVariableTypeDeserialization() throws Exception {
List<Integer> list = new ArrayList<>();
list.add(4);
Expand All @@ -227,6 +246,7 @@ public void testParameterizedTypeWithVariableTypeDeserialization() throws Except
assertEquals(objAfterDeserialization.getExpectedJson(), json);
}

@Test
public void testParameterizedTypeGenericArraysSerialization() throws Exception {
List<Integer> list = new ArrayList<>();
list.add(1);
Expand All @@ -240,6 +260,7 @@ public void testParameterizedTypeGenericArraysSerialization() throws Exception {
assertEquals("{\"arrayOfListOfTypeParameters\":[[1,2],[1,2]]}", json);
}

@Test
public void testParameterizedTypeGenericArraysDeserialization() throws Exception {
List<Integer> list = new ArrayList<>();
list.add(1);
Expand Down Expand Up @@ -483,18 +504,63 @@ public static final class Amount<Q extends Quantity>
int value = 30;
}

@Test
public void testDeepParameterizedTypeSerialization() {
Amount<MyQuantity> amount = new Amount<>();
String json = gson.toJson(amount);
assertTrue(json.contains("value"));
assertTrue(json.contains("30"));
}

@Test
public void testDeepParameterizedTypeDeserialization() {
String json = "{value:30}";
Type type = new TypeToken<Amount<MyQuantity>>() {}.getType();
Amount<MyQuantity> amount = gson.fromJson(json, type);
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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@
*/
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();
Expand All @@ -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<reps; ++i) {
gson.fromJson(json, LIST_TYPE);
gson.fromJson(json, LIST_TYPE_TOKEN);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ private enum Document {
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;
}
}
Expand Down