Skip to content

Commit ebc3ec5

Browse files
fix: comprehend deserializtion of primitive types (#1007)
Resolves: #997
1 parent bc35dab commit ebc3ec5

File tree

2 files changed

+104
-7
lines changed

2 files changed

+104
-7
lines changed

aws-api-appsync/src/main/java/com/amplifyframework/core/model/types/GsonJavaTypeAdapters.java

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,45 @@ public Class<?> read(JsonReader jsonReader) throws IOException {
101101
jsonReader.nextNull();
102102
return null;
103103
}
104-
final Class<?> clazz;
104+
String classString = jsonReader.nextString();
105105
try {
106-
clazz = Class.forName(jsonReader.nextString());
106+
return Class.forName(classString);
107107
} catch (ClassNotFoundException exception) {
108-
throw new IOException(exception);
108+
// Class.forName() doesn't support primitives.
109+
// So, we'll try that next, before giving up.
110+
}
111+
try {
112+
return boxForPrimitiveLabel(classString);
113+
} catch (IllegalArgumentException illegalArgumentException) {
114+
// At this point, we've tried to load the class,
115+
// and also tried to box up a primitive, but neither have worked.
116+
throw new IOException("Unable to deserialize class for " + classString);
117+
}
118+
}
119+
120+
private static Class<?> boxForPrimitiveLabel(String primitiveLabel) {
121+
switch (primitiveLabel) {
122+
case "boolean":
123+
return Boolean.class;
124+
case "byte":
125+
return Byte.class;
126+
case "short":
127+
return Short.class;
128+
case "int":
129+
return Integer.class;
130+
case "long":
131+
return Long.class;
132+
case "float":
133+
return Float.class;
134+
case "double":
135+
return Double.class;
136+
case "char":
137+
return Character.class;
138+
case "void":
139+
return Void.class;
140+
default:
141+
throw new IllegalArgumentException("No primitive with name = " + primitiveLabel);
109142
}
110-
return clazz;
111143
}
112144
}
113145
}

aws-api-appsync/src/test/java/com/amplifyframework/core/model/types/GsonJavaTypeAdaptersTest.java

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,36 @@
2020

2121
import com.google.gson.Gson;
2222
import com.google.gson.GsonBuilder;
23+
import org.junit.Before;
2324
import org.junit.Test;
2425

26+
import java.util.HashMap;
27+
import java.util.Map;
28+
2529
import static org.junit.Assert.assertEquals;
2630

2731
/**
2832
* Tests the {@link GsonJavaTypeAdapters.ClassTypeAdapterFactory} in isolation.
2933
*/
3034
public final class GsonJavaTypeAdaptersTest {
35+
private Gson gson;
36+
37+
/**
38+
* We'll test the adapter through a Gson instance. Set one up.
39+
*/
40+
@Before
41+
public void setup() {
42+
this.gson = new GsonBuilder()
43+
.registerTypeAdapterFactory(new GsonJavaTypeAdapters.ClassTypeAdapterFactory())
44+
.create();
45+
}
46+
3147
/**
3248
* Start with a class, serialize it, deserialize it, and
3349
* expect the same class back.
3450
*/
3551
@Test
3652
public void serializeAndDeserializeClass() {
37-
Gson gson = new GsonBuilder()
38-
.registerTypeAdapterFactory(new GsonJavaTypeAdapters.ClassTypeAdapterFactory())
39-
.create();
4053
String serialized = gson.toJson(BlogOwner.class);
4154
assertEquals(
4255
Wrap.inDoubleQuotes("com.amplifyframework.testmodels.commentsblog.BlogOwner"),
@@ -45,4 +58,56 @@ public void serializeAndDeserializeClass() {
4558
Class<?> deserialized = gson.fromJson(serialized, Class.class);
4659
assertEquals(BlogOwner.class, deserialized);
4760
}
61+
62+
/**
63+
* Validate that primitive type labels (e.g. "int") can be derialized to their
64+
* corresponding boxed class types.
65+
*/
66+
@Test
67+
public void deserializePrimitiveLabel() {
68+
Map<String, Class<?>> primitiveNameToBoxedType = new HashMap<>();
69+
primitiveNameToBoxedType.put("int", Integer.class);
70+
primitiveNameToBoxedType.put("short", Short.class);
71+
primitiveNameToBoxedType.put("long", Long.class);
72+
primitiveNameToBoxedType.put("boolean", Boolean.class);
73+
primitiveNameToBoxedType.put("byte", Byte.class);
74+
primitiveNameToBoxedType.put("char", Character.class);
75+
primitiveNameToBoxedType.put("float", Float.class);
76+
primitiveNameToBoxedType.put("double", Double.class);
77+
primitiveNameToBoxedType.put("void", Void.class);
78+
79+
for (Map.Entry<String, Class<?>> entry : primitiveNameToBoxedType.entrySet()) {
80+
assertEquals(
81+
"Primitive comprehension failed for " + entry.getKey(),
82+
entry.getValue(),
83+
gson.fromJson(entry.getKey(), Class.class)
84+
);
85+
}
86+
}
87+
88+
/**
89+
* If a user inadvertently provided a primitive type, we just box it.
90+
*/
91+
@Test
92+
public void serializePrimitiveRetrieveClass() {
93+
Map<Class<?>, Class<?>> primitiveTypeToBoxedType = new HashMap<>();
94+
primitiveTypeToBoxedType.put(int.class, Integer.class);
95+
primitiveTypeToBoxedType.put(short.class, Short.class);
96+
primitiveTypeToBoxedType.put(long.class, Long.class);
97+
primitiveTypeToBoxedType.put(boolean.class, Boolean.class);
98+
primitiveTypeToBoxedType.put(byte.class, Byte.class);
99+
primitiveTypeToBoxedType.put(char.class, Character.class);
100+
primitiveTypeToBoxedType.put(float.class, Float.class);
101+
primitiveTypeToBoxedType.put(double.class, Double.class);
102+
primitiveTypeToBoxedType.put(void.class, Void.class);
103+
104+
for (Map.Entry<Class<?>, Class<?>> entry : primitiveTypeToBoxedType.entrySet()) {
105+
String serialized = gson.toJson(entry.getKey());
106+
assertEquals(
107+
"Primitive comprehension failed for " + serialized,
108+
entry.getValue(),
109+
gson.fromJson(serialized, Class.class)
110+
);
111+
}
112+
}
48113
}

0 commit comments

Comments
 (0)