• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.gson.functional;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertTrue;
21 
22 import com.google.gson.Gson;
23 import com.google.gson.GsonBuilder;
24 import com.google.gson.JsonArray;
25 import com.google.gson.JsonObject;
26 import com.google.gson.ParameterizedTypeFixtures.MyParameterizedType;
27 import com.google.gson.ParameterizedTypeFixtures.MyParameterizedTypeAdapter;
28 import com.google.gson.ParameterizedTypeFixtures.MyParameterizedTypeInstanceCreator;
29 import com.google.gson.common.TestTypes.BagOfPrimitives;
30 import com.google.gson.reflect.TypeToken;
31 import com.google.gson.stream.JsonReader;
32 import java.io.Reader;
33 import java.io.Serializable;
34 import java.io.StringReader;
35 import java.io.StringWriter;
36 import java.io.Writer;
37 import java.lang.reflect.Type;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.List;
41 import org.junit.Before;
42 import org.junit.Test;
43 
44 /**
45  * Functional tests for the serialization and deserialization of parameterized types in Gson.
46  *
47  * @author Inderjeet Singh
48  * @author Joel Leitch
49  */
50 public class ParameterizedTypesTest {
51   private Gson gson;
52 
53   @Before
setUp()54   public void setUp() {
55     gson = new Gson();
56   }
57 
58   @Test
testParameterizedTypesSerialization()59   public void testParameterizedTypesSerialization() throws Exception {
60     MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
61     Type typeOfSrc = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
62     String json = gson.toJson(src, typeOfSrc);
63     assertEquals(src.getExpectedJson(), json);
64   }
65 
66   @Test
testParameterizedTypeDeserialization()67   public void testParameterizedTypeDeserialization() throws Exception {
68     BagOfPrimitives bag = new BagOfPrimitives();
69     MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<>(bag);
70     Type expectedType = new TypeToken<MyParameterizedType<BagOfPrimitives>>() {}.getType();
71     BagOfPrimitives bagDefaultInstance = new BagOfPrimitives();
72     Gson gson = new GsonBuilder().registerTypeAdapter(
73         expectedType, new MyParameterizedTypeInstanceCreator<>(bagDefaultInstance))
74         .create();
75 
76     String json = expected.getExpectedJson();
77     MyParameterizedType<BagOfPrimitives> actual = gson.fromJson(json, expectedType);
78     assertEquals(expected, actual);
79   }
80 
81   @Test
testTypesWithMultipleParametersSerialization()82   public void testTypesWithMultipleParametersSerialization() throws Exception {
83     MultiParameters<Integer, Float, Double, String, BagOfPrimitives> src =
84         new MultiParameters<>(10, 1.0F, 2.1D, "abc", new BagOfPrimitives());
85     Type typeOfSrc = new TypeToken<MultiParameters<Integer, Float, Double, String,
86         BagOfPrimitives>>() {}.getType();
87     String json = gson.toJson(src, typeOfSrc);
88     String expected = "{\"a\":10,\"b\":1.0,\"c\":2.1,\"d\":\"abc\","
89         + "\"e\":{\"longValue\":0,\"intValue\":0,\"booleanValue\":false,\"stringValue\":\"\"}}";
90     assertEquals(expected, json);
91   }
92 
93   @Test
testTypesWithMultipleParametersDeserialization()94   public void testTypesWithMultipleParametersDeserialization() throws Exception {
95     Type typeOfTarget = new TypeToken<MultiParameters<Integer, Float, Double, String,
96         BagOfPrimitives>>() {}.getType();
97     String json = "{\"a\":10,\"b\":1.0,\"c\":2.1,\"d\":\"abc\","
98         + "\"e\":{\"longValue\":0,\"intValue\":0,\"booleanValue\":false,\"stringValue\":\"\"}}";
99     MultiParameters<Integer, Float, Double, String, BagOfPrimitives> target =
100         gson.fromJson(json, typeOfTarget);
101     MultiParameters<Integer, Float, Double, String, BagOfPrimitives> expected =
102         new MultiParameters<>(10, 1.0F, 2.1D, "abc", new BagOfPrimitives());
103     assertEquals(expected, target);
104   }
105 
106   @Test
testParameterizedTypeWithCustomSerializer()107   public void testParameterizedTypeWithCustomSerializer() {
108     Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
109     Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
110     Gson gson = new GsonBuilder()
111         .registerTypeAdapter(ptIntegerType, new MyParameterizedTypeAdapter<Integer>())
112         .registerTypeAdapter(ptStringType, new MyParameterizedTypeAdapter<String>())
113         .create();
114     MyParameterizedType<Integer> intTarget = new MyParameterizedType<>(10);
115     String json = gson.toJson(intTarget, ptIntegerType);
116     assertEquals(MyParameterizedTypeAdapter.<Integer>getExpectedJson(intTarget), json);
117 
118     MyParameterizedType<String> stringTarget = new MyParameterizedType<>("abc");
119     json = gson.toJson(stringTarget, ptStringType);
120     assertEquals(MyParameterizedTypeAdapter.<String>getExpectedJson(stringTarget), json);
121   }
122 
123   @Test
testParameterizedTypesWithCustomDeserializer()124   public void testParameterizedTypesWithCustomDeserializer() {
125     Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
126     Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
127     Gson gson = new GsonBuilder().registerTypeAdapter(
128         ptIntegerType, new MyParameterizedTypeAdapter<Integer>())
129         .registerTypeAdapter(ptStringType, new MyParameterizedTypeAdapter<String>())
130         .registerTypeAdapter(ptStringType, new MyParameterizedTypeInstanceCreator<>(""))
131         .registerTypeAdapter(ptIntegerType, new MyParameterizedTypeInstanceCreator<>(0))
132         .create();
133 
134     MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
135     String json = MyParameterizedTypeAdapter.<Integer>getExpectedJson(src);
136     MyParameterizedType<Integer> intTarget = gson.fromJson(json, ptIntegerType);
137     assertEquals(10, intTarget.value.intValue());
138 
139     MyParameterizedType<String> srcStr = new MyParameterizedType<>("abc");
140     json = MyParameterizedTypeAdapter.<String>getExpectedJson(srcStr);
141     MyParameterizedType<String> stringTarget = gson.fromJson(json, ptStringType);
142     assertEquals("abc", stringTarget.value);
143   }
144 
145   @Test
testParameterizedTypesWithWriterSerialization()146   public void testParameterizedTypesWithWriterSerialization() throws Exception {
147     Writer writer = new StringWriter();
148     MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
149     Type typeOfSrc = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
150     gson.toJson(src, typeOfSrc, writer);
151     assertEquals(src.getExpectedJson(), writer.toString());
152   }
153 
154   @Test
testParameterizedTypeWithReaderDeserialization()155   public void testParameterizedTypeWithReaderDeserialization() throws Exception {
156     BagOfPrimitives bag = new BagOfPrimitives();
157     MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<>(bag);
158     Type expectedType = new TypeToken<MyParameterizedType<BagOfPrimitives>>() {}.getType();
159     BagOfPrimitives bagDefaultInstance = new BagOfPrimitives();
160     Gson gson = new GsonBuilder().registerTypeAdapter(
161         expectedType, new MyParameterizedTypeInstanceCreator<>(bagDefaultInstance))
162         .create();
163 
164     Reader json = new StringReader(expected.getExpectedJson());
165     MyParameterizedType<Integer> actual = gson.fromJson(json, expectedType);
166     assertEquals(expected, actual);
167   }
168 
169   @SuppressWarnings("varargs")
170   @SafeVarargs
arrayOf(T... args)171   private static <T> T[] arrayOf(T... args) {
172     return args;
173   }
174 
175   @Test
testVariableTypeFieldsAndGenericArraysSerialization()176   public void testVariableTypeFieldsAndGenericArraysSerialization() throws Exception {
177     Integer obj = 0;
178     Integer[] array = { 1, 2, 3 };
179     List<Integer> list = new ArrayList<>();
180     list.add(4);
181     list.add(5);
182     List<Integer>[] arrayOfLists = arrayOf(list, list);
183 
184     Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
185     ObjectWithTypeVariables<Integer> objToSerialize =
186         new ObjectWithTypeVariables<>(obj, array, list, arrayOfLists, list, arrayOfLists);
187     String json = gson.toJson(objToSerialize, typeOfSrc);
188 
189     assertEquals(objToSerialize.getExpectedJson(), json);
190   }
191 
192   @Test
testVariableTypeFieldsAndGenericArraysDeserialization()193   public void testVariableTypeFieldsAndGenericArraysDeserialization() throws Exception {
194     Integer obj = 0;
195     Integer[] array = { 1, 2, 3 };
196     List<Integer> list = new ArrayList<>();
197     list.add(4);
198     list.add(5);
199     List<Integer>[] arrayOfLists = arrayOf(list, list);
200 
201     Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
202     ObjectWithTypeVariables<Integer> objToSerialize =
203         new ObjectWithTypeVariables<>(obj, array, list, arrayOfLists, list, arrayOfLists);
204     String json = gson.toJson(objToSerialize, typeOfSrc);
205     ObjectWithTypeVariables<Integer> objAfterDeserialization = gson.fromJson(json, typeOfSrc);
206 
207     assertEquals(objAfterDeserialization.getExpectedJson(), json);
208   }
209 
210   @Test
testVariableTypeDeserialization()211   public void testVariableTypeDeserialization() throws Exception {
212     Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
213     ObjectWithTypeVariables<Integer> objToSerialize =
214         new ObjectWithTypeVariables<>(0, null, null, null, null, null);
215     String json = gson.toJson(objToSerialize, typeOfSrc);
216     ObjectWithTypeVariables<Integer> objAfterDeserialization = gson.fromJson(json, typeOfSrc);
217 
218     assertEquals(objAfterDeserialization.getExpectedJson(), json);
219   }
220 
221   @Test
testVariableTypeArrayDeserialization()222   public void testVariableTypeArrayDeserialization() throws Exception {
223     Integer[] array = { 1, 2, 3 };
224 
225     Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
226     ObjectWithTypeVariables<Integer> objToSerialize =
227         new ObjectWithTypeVariables<>(null, array, null, null, null, null);
228     String json = gson.toJson(objToSerialize, typeOfSrc);
229     ObjectWithTypeVariables<Integer> objAfterDeserialization = gson.fromJson(json, typeOfSrc);
230 
231     assertEquals(objAfterDeserialization.getExpectedJson(), json);
232   }
233 
234   @Test
testParameterizedTypeWithVariableTypeDeserialization()235   public void testParameterizedTypeWithVariableTypeDeserialization() throws Exception {
236     List<Integer> list = new ArrayList<>();
237     list.add(4);
238     list.add(5);
239 
240     Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
241     ObjectWithTypeVariables<Integer> objToSerialize =
242         new ObjectWithTypeVariables<>(null, null, list, null, null, null);
243     String json = gson.toJson(objToSerialize, typeOfSrc);
244     ObjectWithTypeVariables<Integer> objAfterDeserialization = gson.fromJson(json, typeOfSrc);
245 
246     assertEquals(objAfterDeserialization.getExpectedJson(), json);
247   }
248 
249   @Test
testParameterizedTypeGenericArraysSerialization()250   public void testParameterizedTypeGenericArraysSerialization() throws Exception {
251     List<Integer> list = new ArrayList<>();
252     list.add(1);
253     list.add(2);
254     List<Integer>[] arrayOfLists = arrayOf(list, list);
255 
256     Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
257     ObjectWithTypeVariables<Integer> objToSerialize =
258         new ObjectWithTypeVariables<>(null, null, null, arrayOfLists, null, null);
259     String json = gson.toJson(objToSerialize, typeOfSrc);
260     assertEquals("{\"arrayOfListOfTypeParameters\":[[1,2],[1,2]]}", json);
261   }
262 
263   @Test
testParameterizedTypeGenericArraysDeserialization()264   public void testParameterizedTypeGenericArraysDeserialization() throws Exception {
265     List<Integer> list = new ArrayList<>();
266     list.add(1);
267     list.add(2);
268     List<Integer>[] arrayOfLists = arrayOf(list, list);
269 
270     Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
271     ObjectWithTypeVariables<Integer> objToSerialize =
272         new ObjectWithTypeVariables<>(null, null, null, arrayOfLists, null, null);
273     String json = gson.toJson(objToSerialize, typeOfSrc);
274     ObjectWithTypeVariables<Integer> objAfterDeserialization = gson.fromJson(json, typeOfSrc);
275 
276     assertEquals(objAfterDeserialization.getExpectedJson(), json);
277   }
278 
279   /**
280    * An test object that has fields that are type variables.
281    *
282    * @param <T> Enforce T to be a string to make writing the "toExpectedJson" method easier.
283    */
284   private static class ObjectWithTypeVariables<T extends Number> {
285     private final T typeParameterObj;
286     private final T[] typeParameterArray;
287     private final List<T> listOfTypeParameters;
288     private final List<T>[] arrayOfListOfTypeParameters;
289     private final List<? extends T> listOfWildcardTypeParameters;
290     private final List<? extends T>[] arrayOfListOfWildcardTypeParameters;
291 
292     // For use by Gson
293     @SuppressWarnings("unused")
ObjectWithTypeVariables()294     private ObjectWithTypeVariables() {
295       this(null, null, null, null, null, null);
296     }
297 
ObjectWithTypeVariables(T obj, T[] array, List<T> list, List<T>[] arrayOfList, List<? extends T> wildcardList, List<? extends T>[] arrayOfWildcardList)298     public ObjectWithTypeVariables(T obj, T[] array, List<T> list, List<T>[] arrayOfList,
299         List<? extends T> wildcardList, List<? extends T>[] arrayOfWildcardList) {
300       this.typeParameterObj = obj;
301       this.typeParameterArray = array;
302       this.listOfTypeParameters = list;
303       this.arrayOfListOfTypeParameters = arrayOfList;
304       this.listOfWildcardTypeParameters = wildcardList;
305       this.arrayOfListOfWildcardTypeParameters = arrayOfWildcardList;
306     }
307 
getExpectedJson()308     public String getExpectedJson() {
309       StringBuilder sb = new StringBuilder().append("{");
310 
311       boolean needsComma = false;
312       if (typeParameterObj != null) {
313         sb.append("\"typeParameterObj\":").append(toString(typeParameterObj));
314         needsComma = true;
315       }
316 
317       if (typeParameterArray != null) {
318         if (needsComma) {
319           sb.append(',');
320         }
321         sb.append("\"typeParameterArray\":[");
322         appendObjectsToBuilder(sb, Arrays.asList(typeParameterArray));
323         sb.append(']');
324         needsComma = true;
325       }
326 
327       if (listOfTypeParameters != null) {
328         if (needsComma) {
329           sb.append(',');
330         }
331         sb.append("\"listOfTypeParameters\":[");
332         appendObjectsToBuilder(sb, listOfTypeParameters);
333         sb.append(']');
334         needsComma = true;
335       }
336 
337       if (arrayOfListOfTypeParameters != null) {
338         if (needsComma) {
339           sb.append(',');
340         }
341         sb.append("\"arrayOfListOfTypeParameters\":[");
342         appendObjectsToBuilder(sb, arrayOfListOfTypeParameters);
343         sb.append(']');
344         needsComma = true;
345       }
346 
347       if (listOfWildcardTypeParameters != null) {
348         if (needsComma) {
349           sb.append(',');
350         }
351         sb.append("\"listOfWildcardTypeParameters\":[");
352         appendObjectsToBuilder(sb, listOfWildcardTypeParameters);
353         sb.append(']');
354         needsComma = true;
355       }
356 
357       if (arrayOfListOfWildcardTypeParameters != null) {
358         if (needsComma) {
359           sb.append(',');
360         }
361         sb.append("\"arrayOfListOfWildcardTypeParameters\":[");
362         appendObjectsToBuilder(sb, arrayOfListOfWildcardTypeParameters);
363         sb.append(']');
364         needsComma = true;
365       }
366       sb.append('}');
367       return sb.toString();
368     }
369 
appendObjectsToBuilder(StringBuilder sb, Iterable<? extends T> iterable)370     private void appendObjectsToBuilder(StringBuilder sb, Iterable<? extends T> iterable) {
371       boolean isFirst = true;
372       for (T obj : iterable) {
373         if (!isFirst) {
374           sb.append(',');
375         }
376         isFirst = false;
377         sb.append(toString(obj));
378       }
379     }
380 
appendObjectsToBuilder(StringBuilder sb, List<? extends T>[] arrayOfList)381     private void appendObjectsToBuilder(StringBuilder sb, List<? extends T>[] arrayOfList) {
382       boolean isFirst = true;
383       for (List<? extends T> list : arrayOfList) {
384         if (!isFirst) {
385           sb.append(',');
386         }
387         isFirst = false;
388         if (list != null) {
389           sb.append('[');
390           appendObjectsToBuilder(sb, list);
391           sb.append(']');
392         } else {
393           sb.append("null");
394         }
395       }
396     }
397 
toString(T obj)398     public String toString(T obj) {
399       return obj.toString();
400     }
401   }
402 
403   private static class MultiParameters<A, B, C, D, E> {
404     A a;
405     B b;
406     C c;
407     D d;
408     E e;
409     // For use by Gson
410     @SuppressWarnings("unused")
MultiParameters()411     private MultiParameters() {
412     }
MultiParameters(A a, B b, C c, D d, E e)413     MultiParameters(A a, B b, C c, D d, E e) {
414       super();
415       this.a = a;
416       this.b = b;
417       this.c = c;
418       this.d = d;
419       this.e = e;
420     }
421     @Override
hashCode()422     public int hashCode() {
423       final int prime = 31;
424       int result = 1;
425       result = prime * result + ((a == null) ? 0 : a.hashCode());
426       result = prime * result + ((b == null) ? 0 : b.hashCode());
427       result = prime * result + ((c == null) ? 0 : c.hashCode());
428       result = prime * result + ((d == null) ? 0 : d.hashCode());
429       result = prime * result + ((e == null) ? 0 : e.hashCode());
430       return result;
431     }
432     @Override
433     @SuppressWarnings("unchecked")
equals(Object obj)434     public boolean equals(Object obj) {
435       if (this == obj) {
436         return true;
437       }
438       if (obj == null) {
439         return false;
440       }
441       if (getClass() != obj.getClass()) {
442         return false;
443       }
444       MultiParameters<A, B, C, D, E> other = (MultiParameters<A, B, C, D, E>) obj;
445       if (a == null) {
446         if (other.a != null) {
447           return false;
448         }
449       } else if (!a.equals(other.a)) {
450         return false;
451       }
452       if (b == null) {
453         if (other.b != null) {
454           return false;
455         }
456       } else if (!b.equals(other.b)) {
457         return false;
458       }
459       if (c == null) {
460         if (other.c != null) {
461           return false;
462         }
463       } else if (!c.equals(other.c)) {
464         return false;
465       }
466       if (d == null) {
467         if (other.d != null) {
468           return false;
469         }
470       } else if (!d.equals(other.d)) {
471         return false;
472       }
473       if (e == null) {
474         if (other.e != null) {
475           return false;
476         }
477       } else if (!e.equals(other.e)) {
478         return false;
479       }
480       return true;
481     }
482   }
483 
484   // Begin: tests to reproduce issue 103
485   private static class Quantity {
486     @SuppressWarnings("unused")
487     int q = 10;
488   }
489   private static class MyQuantity extends Quantity {
490     @SuppressWarnings("unused")
491     int q2 = 20;
492   }
493   private interface Measurable<T> {
494   }
495   private interface Field<T> {
496   }
497   private interface Immutable {
498   }
499 
500   public static final class Amount<Q extends Quantity>
501       implements Measurable<Q>, Field<Amount<?>>, Serializable, Immutable {
502     private static final long serialVersionUID = -7560491093120970437L;
503 
504     int value = 30;
505   }
506 
507   @Test
testDeepParameterizedTypeSerialization()508   public void testDeepParameterizedTypeSerialization() {
509     Amount<MyQuantity> amount = new Amount<>();
510     String json = gson.toJson(amount);
511     assertTrue(json.contains("value"));
512     assertTrue(json.contains("30"));
513   }
514 
515   @Test
testDeepParameterizedTypeDeserialization()516   public void testDeepParameterizedTypeDeserialization() {
517     String json = "{value:30}";
518     Type type = new TypeToken<Amount<MyQuantity>>() {}.getType();
519     Amount<MyQuantity> amount = gson.fromJson(json, type);
520     assertEquals(30, amount.value);
521   }
522   // End: tests to reproduce issue 103
523 
assertCorrectlyDeserialized(Object object)524   private static void assertCorrectlyDeserialized(Object object) {
525     @SuppressWarnings("unchecked")
526     List<Quantity> list = (List<Quantity>) object;
527     assertEquals(1, list.size());
528     assertEquals(4, list.get(0).q);
529   }
530 
531   @Test
testGsonFromJsonTypeToken()532   public void testGsonFromJsonTypeToken() {
533     TypeToken<List<Quantity>> typeToken = new TypeToken<List<Quantity>>() {};
534     Type type = typeToken.getType();
535 
536     {
537       JsonObject jsonObject = new JsonObject();
538       jsonObject.addProperty("q", 4);
539       JsonArray jsonArray = new JsonArray();
540       jsonArray.add(jsonObject);
541 
542       assertCorrectlyDeserialized(gson.fromJson(jsonArray, typeToken));
543       assertCorrectlyDeserialized(gson.fromJson(jsonArray, type));
544     }
545 
546     String json = "[{\"q\":4}]";
547 
548     {
549       assertCorrectlyDeserialized(gson.fromJson(json, typeToken));
550       assertCorrectlyDeserialized(gson.fromJson(json, type));
551     }
552 
553     {
554       assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), typeToken));
555       assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), type));
556     }
557 
558     {
559       JsonReader reader = new JsonReader(new StringReader(json));
560       assertCorrectlyDeserialized(gson.fromJson(reader, typeToken));
561 
562       reader = new JsonReader(new StringReader(json));
563       assertCorrectlyDeserialized(gson.fromJson(reader, type));
564     }
565   }
566 }
567