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