Skip to content

Commit 929645c

Browse files
committed
support nested generics
1 parent 827d944 commit 929645c

File tree

10 files changed

+136
-79
lines changed

10 files changed

+136
-79
lines changed

src/main/java/com/jsoniter/Codegen.java

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ private static String genSource(String cacheKey, Class clazz, Type[] typeArgs) {
139139

140140
private static String genMap(Class clazz, Type valueType) {
141141
StringBuilder lines = new StringBuilder();
142-
append(lines, "public Object decode(java.lang.reflect.Type type, com.jsoniter.Jsoniter iter) {");
142+
append(lines, "public Object decode(com.jsoniter.Jsoniter iter) {");
143143
append(lines, "{{clazz}} map = new {{clazz}}();");
144144
append(lines, "for (String field = iter.readObject(); field != null; field = iter.readObject()) {");
145145
append(lines, "map.put(field, {{op}});");
@@ -151,7 +151,7 @@ private static String genMap(Class clazz, Type valueType) {
151151

152152
private static String genNative(Class clazz) {
153153
StringBuilder lines = new StringBuilder();
154-
append(lines, "public Object decode(java.lang.reflect.Type type, com.jsoniter.Jsoniter iter) {");
154+
append(lines, "public Object decode(com.jsoniter.Jsoniter iter) {");
155155
append(lines, "return " + NATIVE_READS.get(clazz.getName()) + ";");
156156
append(lines, "}");
157157
return lines.toString();
@@ -197,15 +197,15 @@ private static String genObject(Class clazz, String cacheKey) {
197197
}
198198
if (map.isEmpty()) {
199199
StringBuilder lines = new StringBuilder();
200-
append(lines, "public Object decode(java.lang.reflect.Type type, com.jsoniter.Jsoniter iter) {");
200+
append(lines, "public Object decode(com.jsoniter.Jsoniter iter) {");
201201
append(lines, "{{clazz}} obj = new {{clazz}}();");
202202
append(lines, "iter.skip();");
203203
append(lines, "return obj;");
204204
append(lines, "}");
205205
return lines.toString().replace("{{clazz}}", clazz.getName());
206206
}
207207
StringBuilder lines = new StringBuilder();
208-
append(lines, "public Object decode(java.lang.reflect.Type type, com.jsoniter.Jsoniter iter) {");
208+
append(lines, "public Object decode(com.jsoniter.Jsoniter iter) {");
209209
append(lines, "{{clazz}} obj = new {{clazz}}();");
210210
append(lines, "for (com.jsoniter.Slice field = iter.readObjectAsSlice(); field != null; field = iter.readObjectAsSlice()) {");
211211
append(lines, "switch (field.len) {");
@@ -334,18 +334,9 @@ private static String genReadOp(Type type) {
334334
ParameterizedType pType = (ParameterizedType) type;
335335
Class clazz = (Class) pType.getRawType();
336336
Type[] args = pType.getActualTypeArguments();
337-
switch (args.length) {
338-
case 1:
339-
return String.format("(%s)iter.read(\"%s\", %s.class, %s.class);",
340-
clazz.getCanonicalName(), TypeLiteral.generateCacheKey(type),
341-
clazz.getCanonicalName(), ((Class) args[0]).getCanonicalName());
342-
case 2:
343-
return String.format("(%s)iter.read(\"%s\", %s.class, %s.class, %s.class);",
344-
clazz.getCanonicalName(), TypeLiteral.generateCacheKey(type),
345-
clazz.getCanonicalName(), ((Class) args[0]).getCanonicalName(), ((Class) args[1]).getCanonicalName());
346-
default:
347-
throw new IllegalArgumentException("unsupported number of type arguments: " + type);
348-
}
337+
String cacheKey = TypeLiteral.generateCacheKey(type);
338+
getDecoder(cacheKey, clazz, args); // set the decoder to cache
339+
return String.format("(%s)iter.read(\"%s\")", clazz.getCanonicalName(), cacheKey);
349340
}
350341
throw new IllegalArgumentException("unsupported type: " + type);
351342
}
@@ -356,7 +347,7 @@ private static String genArray(Class clazz) {
356347
throw new IllegalArgumentException("nested array not supported: " + clazz.getCanonicalName());
357348
}
358349
StringBuilder lines = new StringBuilder();
359-
append(lines, "public Object decode(java.lang.reflect.Type type, com.jsoniter.Jsoniter iter) {");
350+
append(lines, "public Object decode(com.jsoniter.Jsoniter iter) {");
360351
append(lines, "if (!iter.readArray()) {");
361352
append(lines, "return new {{comp}}[0];");
362353
append(lines, "}");
@@ -398,7 +389,7 @@ private static String genArray(Class clazz) {
398389

399390
private static String genCollectionWithCapacity(Class clazz, Type compType) {
400391
StringBuilder lines = new StringBuilder();
401-
append(lines, "public Object decode(java.lang.reflect.Type type, com.jsoniter.Jsoniter iter) {");
392+
append(lines, "public Object decode(com.jsoniter.Jsoniter iter) {");
402393
append(lines, "if (!iter.readArray()) {");
403394
append(lines, "return new {{clazz}}(0);");
404395
append(lines, "}");
@@ -442,7 +433,7 @@ private static String genCollectionWithCapacity(Class clazz, Type compType) {
442433

443434
private static String genCollection(Class clazz, Type compType) {
444435
StringBuilder lines = new StringBuilder();
445-
append(lines, "public Object decode(java.lang.reflect.Type type, com.jsoniter.Jsoniter iter) {");
436+
append(lines, "public Object decode(com.jsoniter.Jsoniter iter) {");
446437
append(lines, "if (!iter.readArray()) {");
447438
append(lines, "return new {{clazz}}();");
448439
append(lines, "}");

src/main/java/com/jsoniter/Decoder.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
package com.jsoniter;
22

33
import java.io.IOException;
4-
import java.lang.reflect.Type;
54

65
public interface Decoder {
76
/**
87
* Customized decoder to read values from iterator
98
*
10-
* @param type the type of object we are setting value to
119
* @param iter the iterator instance
1210
* @return the value to set
1311
* @throws IOException
1412
*/
15-
Object decode(Type type, Jsoniter iter) throws IOException;
13+
Object decode(Jsoniter iter) throws IOException;
1614

1715
interface BooleanDecoder extends Decoder {
1816
boolean decodeBoolean(Jsoniter iter) throws IOException;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.jsoniter;
2+
3+
import java.lang.reflect.Field;
4+
5+
public class EmptyExtension implements Extension {
6+
7+
@Override
8+
public Decoder createDecoder(Field field) {
9+
return null;
10+
}
11+
12+
@Override
13+
public String[] getAlternativeFieldNames(Field field) {
14+
return null;
15+
}
16+
}

src/main/java/com/jsoniter/Jsoniter.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -797,24 +797,16 @@ public final Object readAnyObject() throws IOException {
797797
}
798798

799799
public final <T> T read(Class<T> clazz) throws IOException {
800-
return (T) Codegen.getDecoder(TypeLiteral.generateCacheKey(clazz), clazz).decode(clazz, this);
800+
return (T) Codegen.getDecoder(TypeLiteral.generateCacheKey(clazz), clazz).decode(this);
801801
}
802802

803803
public final <T> T read(TypeLiteral<T> typeLiteral) throws IOException {
804804
Type type = typeLiteral.getType();
805-
return (T) Codegen.getDecoder(typeLiteral.cacheKey, type).decode(type, this);
805+
return (T) Codegen.getDecoder(typeLiteral.cacheKey, type).decode(this);
806806
}
807807

808-
public final <T> T read(String cacheKey, Class<T> clazz) throws IOException {
809-
return (T) Codegen.getDecoder(cacheKey, clazz).decode(clazz, this);
810-
}
811-
812-
public final <T> T read(String cacheKey, Class<T> clazz, Type typeArg1) throws IOException {
813-
return (T) Codegen.getDecoder(cacheKey, clazz, typeArg1).decode(clazz, this);
814-
}
815-
816-
public final <T> T read(String cacheKey, Class<T> clazz, Type typeArg1, Type typeArg2) throws IOException {
817-
return (T) Codegen.getDecoder(cacheKey, clazz, typeArg1, typeArg2).decode(clazz, this);
808+
public final <T> T read(String cacheKey) throws IOException {
809+
return (T) Codegen.getDecoder(cacheKey, null).decode(this);
818810
}
819811

820812
public final void skip() throws IOException {

src/main/java/com/jsoniter/TypeLiteral.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.jsoniter;
22

3+
import java.io.IOException;
34
import java.lang.reflect.ParameterizedType;
45
import java.lang.reflect.Type;
56

@@ -31,16 +32,33 @@ public static String generateCacheKey(Type type) {
3132
Class clazz = (Class) pType.getRawType();
3233
decoderClassName.append(clazz.getName().replace("[", "array_"));
3334
for (int i = 0; i < pType.getActualTypeArguments().length; i++) {
34-
Class typeArg = (Class) pType.getActualTypeArguments()[i];
35+
String typeName = formatTypeWithoutSpecialCharacter(pType.getActualTypeArguments()[i]);
3536
decoderClassName.append('_');
36-
decoderClassName.append(typeArg.getName());
37+
decoderClassName.append(typeName);
3738
}
3839
} else {
3940
throw new UnsupportedOperationException("do not know how to handle: " + type);
4041
}
4142
return decoderClassName.toString().replace("$", "_");
4243
}
4344

45+
private static String formatTypeWithoutSpecialCharacter(Type type) {
46+
if (type instanceof Class) {
47+
Class clazz = (Class) type;
48+
return clazz.getCanonicalName();
49+
}
50+
if (type instanceof ParameterizedType) {
51+
ParameterizedType pType = (ParameterizedType) type;
52+
String typeName = formatTypeWithoutSpecialCharacter(pType.getRawType());
53+
for (Type typeArg : pType.getActualTypeArguments()) {
54+
typeName += "_";
55+
typeName += formatTypeWithoutSpecialCharacter(typeArg);
56+
}
57+
return typeName;
58+
}
59+
throw new RuntimeException("unsupported type: " + type);
60+
}
61+
4462
static Type getSuperclassTypeParameter(Class<?> subclass) {
4563
Type superclass = subclass.getGenericSuperclass();
4664
if (superclass instanceof Class) {

src/test/java/com/jsoniter/ComplexObject.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44

55
public class ComplexObject {
66
public int field1;
7-
public List<Integer> field2;
7+
public List<List<Integer>> field2;
88
public Any field3;
99
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.jsoniter;
2+
3+
public class CtorCustomizedObject {
4+
5+
private final int field1;
6+
7+
public CtorCustomizedObject(int field1) {
8+
this.field1 = field1;
9+
}
10+
11+
public int getField1() {
12+
return field1;
13+
}
14+
}

src/test/java/com/jsoniter/TestCustomize.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44

55
import java.io.IOException;
66
import java.lang.reflect.Field;
7-
import java.lang.reflect.Type;
87
import java.util.Date;
98

109
public class TestCustomize extends TestCase {
1110
public void test_customize_type() throws IOException {
1211
Jsoniter.registerTypeDecoder(Date.class, new Decoder() {
1312
@Override
14-
public Object decode(Type type, Jsoniter iter) throws IOException {
13+
public Object decode(Jsoniter iter) throws IOException {
1514
return new Date(iter.readLong());
1615
}
1716
});
@@ -21,10 +20,10 @@ public Object decode(Type type, Jsoniter iter) throws IOException {
2120
}
2221

2322
public void test_customize_field() throws IOException {
24-
Jsoniter.registerFieldDecoder(CustomizedObject.class, "field1", new Decoder(){
23+
Jsoniter.registerFieldDecoder(CustomizedObject.class, "field1", new Decoder() {
2524

2625
@Override
27-
public Object decode(Type type, Jsoniter iter) throws IOException {
26+
public Object decode(Jsoniter iter) throws IOException {
2827
return Integer.toString(iter.readInt());
2928
}
3029
});
@@ -38,10 +37,10 @@ public void test_customize_all_fields() throws IOException {
3837
@Override
3938
public Decoder createDecoder(Field field) {
4039
if (field.getDeclaringClass() == CustomizedObject.class && field.getName().equals("field1")) {
41-
return new Decoder(){
40+
return new Decoder() {
4241

4342
@Override
44-
public Object decode(Type type, Jsoniter iter) throws IOException {
43+
public Object decode(Jsoniter iter) throws IOException {
4544
return Integer.toString(iter.readInt());
4645
}
4746
};
@@ -66,7 +65,7 @@ public Decoder createDecoder(Field field) {
6665
if (field.getDeclaringClass() == CustomizedObject.class && field.getName().equals("field1")) {
6766
return new Decoder() {
6867
@Override
69-
public Object decode(Type type, Jsoniter iter) throws IOException {
68+
public Object decode(Jsoniter iter) throws IOException {
7069
return Integer.toString(iter.readInt());
7170
}
7271
};
@@ -90,20 +89,26 @@ public String[] getAlternativeFieldNames(Field field) {
9089
}
9190

9291
public void test_customized_decoder_with_int_field() throws IOException {
93-
Jsoniter.registerFieldDecoder(CustomizedObject.class, "field3", new Decoder.IntDecoder(){
92+
Jsoniter.registerFieldDecoder(CustomizedObject.class, "field3", new Decoder.IntDecoder() {
9493

9594
@Override
9695
public int decodeInt(Jsoniter iter) throws IOException {
9796
return iter.readInt() * 5;
9897
}
9998

10099
@Override
101-
public Object decode(Type type, Jsoniter iter) throws IOException {
100+
public Object decode(Jsoniter iter) throws IOException {
102101
return null;
103102
}
104103
});
105104
Jsoniter iter = Jsoniter.parse("{'field3': 100}".replace('\'', '"'));
106105
CustomizedObject myObject = iter.read(CustomizedObject.class);
107106
assertEquals(100 * 5, myObject.field3);
108107
}
108+
109+
public void test_customized_constructor() throws IOException {
110+
Jsoniter iter = Jsoniter.parse("{'field1': 100}".replace('\'', '"'));
111+
CtorCustomizedObject myObject = iter.read(CtorCustomizedObject.class);
112+
assertEquals(100, myObject.getField1());
113+
}
109114
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.jsoniter;
2+
3+
import junit.framework.TestCase;
4+
5+
import java.io.IOException;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Set;
10+
11+
import static org.junit.Assert.assertArrayEquals;
12+
13+
public class TestGenerics extends TestCase {
14+
15+
public void test_int_list() throws IOException {
16+
Jsoniter iter = Jsoniter.parse("[1,2,3]");
17+
List<Integer> val = iter.read(new TypeLiteral<ArrayList<Integer>>() {
18+
});
19+
assertArrayEquals(new Integer[]{1, 2, 3}, val.toArray(new Integer[0]));
20+
}
21+
22+
public void test_string_list() throws IOException {
23+
Jsoniter iter = Jsoniter.parse("['hello', 'world']".replace('\'', '"'));
24+
List<String> val = iter.read(new TypeLiteral<List<String>>() {
25+
});
26+
assertArrayEquals(new String[]{"hello", "world"}, val.toArray(new String[0]));
27+
}
28+
29+
public void test_string_set() throws IOException {
30+
Jsoniter iter = Jsoniter.parse("['hello']".replace('\'', '"'));
31+
Set<String> val = iter.read(new TypeLiteral<Set<String>>() {
32+
});
33+
assertArrayEquals(new String[]{"hello"}, val.toArray(new String[0]));
34+
}
35+
36+
public void test_string_map() throws IOException {
37+
Jsoniter iter = Jsoniter.parse("{'hello': 'world'}".replace('\'', '"'));
38+
Map<String, String> val = iter.read(new TypeLiteral<Map<String, String>>() {
39+
});
40+
assertEquals("world", val.get("hello"));
41+
}
42+
43+
public void test_list_of_list() throws Exception {
44+
Jsoniter iter = Jsoniter.parse("[[1,2],[3,4]]");
45+
List<List<Integer>> listOfList = iter.read(new TypeLiteral<List<List<Integer>>>() {
46+
});
47+
assertEquals(Integer.valueOf(4), listOfList.get(1).get(1));
48+
}
49+
50+
public void test_complex_object() throws IOException {
51+
Jsoniter iter = Jsoniter.parse("{'field1': 100, 'field2': [[1,2],[3,4]]}".replace('\'', '"'));
52+
ComplexObject val = iter.read(ComplexObject.class);
53+
assertEquals(100, val.field1);
54+
assertEquals(Integer.valueOf(4), val.field2.get(1).get(1));
55+
}
56+
}

0 commit comments

Comments
 (0)