Skip to content

Commit 8c72678

Browse files
committed
support map
1 parent e0cace3 commit 8c72678

File tree

3 files changed

+147
-70
lines changed

3 files changed

+147
-70
lines changed

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

Lines changed: 103 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,15 @@ class Codegen {
2828
put(Long.class.getName(), "Long.valueOf(iter.readLong())");
2929
put(String.class.getName(), "iter.readString()");
3030
}};
31-
final static Set<Class> WITH_CAPACITY_COLLECTION_CLASSES = new HashSet<Class>(){{
31+
final static Set<Class> WITH_CAPACITY_COLLECTION_CLASSES = new HashSet<Class>() {{
3232
add(ArrayList.class);
3333
add(HashSet.class);
34+
add(Vector.class);
3435
}};
3536
static volatile Map<String, Decoder> cache = new HashMap<>();
3637
static ClassPool pool = ClassPool.getDefault();
3738

38-
static Decoder getDecoder(String cacheKey, Type type, Type[] typeArgs) {
39+
static Decoder getDecoder(String cacheKey, Type type, Type... typeArgs) {
3940
Decoder decoder = cache.get(cacheKey);
4041
if (decoder != null) {
4142
return decoder;
@@ -56,7 +57,7 @@ private synchronized static Decoder gen(String cacheKey, Type type, Type[] typeA
5657
} else {
5758
clazz = (Class) type;
5859
}
59-
String source = genSource(cacheKey, typeArgs, clazz);
60+
String source = genSource(cacheKey, clazz, typeArgs);
6061
try {
6162
CtClass ctClass = pool.makeClass(cacheKey);
6263
ctClass.setInterfaces(new CtClass[]{pool.get(Decoder.class.getName())});
@@ -72,24 +73,68 @@ private synchronized static Decoder gen(String cacheKey, Type type, Type[] typeA
7273
}
7374
}
7475

75-
private static String genSource(String cacheKey, Type[] typeArgs, Class clazz) {
76+
private static String genSource(String cacheKey, Class clazz, Type[] typeArgs) {
77+
if (Object.class == clazz) {
78+
throw new IllegalArgumentException("can not bind to Object.class, parser need schema");
79+
}
80+
if (NATIVE_READS.containsKey(clazz.getName())) {
81+
return genNative(clazz);
82+
}
7683
if (clazz.isArray()) {
7784
return genArray(clazz);
78-
} else if (Collection.class.isAssignableFrom(clazz)) {
79-
Class compType = (Class) typeArgs[0];
85+
}
86+
if (Map.class.isAssignableFrom(clazz)) {
87+
if (typeArgs.length != 2) {
88+
throw new IllegalArgumentException(
89+
"can not bind to generic collection without argument types, " +
90+
"try syntax like TypeLiteral<Map<String, String>>{}");
91+
}
92+
if (typeArgs[0] != String.class) {
93+
throw new IllegalArgumentException("map key must be String");
94+
}
95+
if (clazz == Map.class) {
96+
clazz = HashMap.class;
97+
}
98+
return genMap(clazz, typeArgs[1]);
99+
}
100+
if (Collection.class.isAssignableFrom(clazz)) {
101+
if (typeArgs.length != 1) {
102+
throw new IllegalArgumentException(
103+
"can not bind to generic collection without argument types, " +
104+
"try syntax like TypeLiteral<List<Integer>>{}");
105+
}
80106
if (clazz == List.class) {
81107
clazz = ArrayList.class;
82108
} else if (clazz == Set.class) {
83109
clazz = HashSet.class;
84110
}
85111
if (WITH_CAPACITY_COLLECTION_CLASSES.contains(clazz)) {
86-
return genCollectionWithCapacity(clazz, compType);
112+
return genCollectionWithCapacity(clazz, typeArgs[0]);
87113
} else {
88-
return genCollection(clazz, compType);
114+
return genCollection(clazz, typeArgs[0]);
89115
}
90-
} else {
91-
return genObject(clazz, cacheKey);
92116
}
117+
return genObject(clazz, cacheKey);
118+
}
119+
120+
private static String genMap(Class clazz, Type valueType) {
121+
StringBuilder lines = new StringBuilder();
122+
append(lines, "public Object decode(java.lang.reflect.Type type, com.github.jsoniter.Jsoniter iter) {");
123+
append(lines, "{{clazz}} map = new {{clazz}}();");
124+
append(lines, "for (String field = iter.readObject(); field != null; field = iter.readObject()) {");
125+
append(lines, "map.put(field, {{op}});");
126+
append(lines, "}");
127+
append(lines, "return map;");
128+
append(lines, "}");
129+
return lines.toString().replace("{{clazz}}", clazz.getName()).replace("{{op}}", genReadOp(valueType));
130+
}
131+
132+
private static String genNative(Class clazz) {
133+
StringBuilder lines = new StringBuilder();
134+
append(lines, "public Object decode(java.lang.reflect.Type type, com.github.jsoniter.Jsoniter iter) {");
135+
append(lines, "return " + NATIVE_READS.get(clazz.getName()) + ";");
136+
append(lines, "}");
137+
return lines.toString();
93138
}
94139

95140
public static void addNewDecoder(String cacheKey, Decoder decoder) {
@@ -164,48 +209,63 @@ private static void addFieldDispatch(StringBuilder lines, int len, int i, Map<By
164209
private static void genField(StringBuilder lines, Field field, String cacheKey) {
165210
String fieldCacheKey = field.getName() + "@" + cacheKey;
166211
String fieldTypeName = field.getType().getCanonicalName();
167-
String nativeRead = NATIVE_READS.get(fieldTypeName);
168212
if (cache.containsKey(fieldCacheKey)) {
169213
append(lines, String.format("obj.%s = (%s)iter.read(\"%s\", %s.class);",
170214
field.getName(), fieldTypeName, fieldCacheKey, fieldTypeName));
171215
return;
172216
}
173-
if (nativeRead == null) {
174-
if (field.getGenericType() instanceof ParameterizedType) {
175-
ParameterizedType pType = (ParameterizedType) field.getGenericType();
176-
Class arg1 = (Class) pType.getActualTypeArguments()[0];
177-
append(lines, String.format("obj.%s = (%s)iter.read(\"%s\", %s.class, %s.class);",
178-
field.getName(), fieldTypeName, TypeLiteral.generateCacheKey(field.getGenericType()),
179-
fieldTypeName, arg1.getName()));
180-
} else {
181-
append(lines, String.format("obj.%s = (%s)iter.read(%s.class);",
182-
field.getName(), fieldTypeName, fieldTypeName));
217+
append(lines, String.format("obj.%s = %s;", field.getName(), genReadOp(field.getGenericType())));
218+
}
219+
220+
private static String genReadOp(Type type) {
221+
if (type instanceof Class) {
222+
Class clazz = (Class) type;
223+
String nativeRead = NATIVE_READS.get(clazz.getName());
224+
if (nativeRead != null) {
225+
return nativeRead;
183226
}
184-
} else {
185-
append(lines, String.format("obj.%s = %s;", field.getName(), nativeRead));
227+
return String.format("(%s)iter.read(\"%s\", %s.class)",
228+
clazz.getName(), TypeLiteral.generateCacheKey(clazz), clazz.getName());
186229
}
230+
if (type instanceof ParameterizedType) {
231+
ParameterizedType pType = (ParameterizedType) type;
232+
Class clazz = (Class) pType.getRawType();
233+
Type[] args = pType.getActualTypeArguments();
234+
switch (args.length) {
235+
case 1:
236+
return String.format("(%s)iter.read(\"%s\", %s.class, %s.class);",
237+
clazz.getName(), TypeLiteral.generateCacheKey(type),
238+
clazz.getName(), ((Class) args[0]).getName());
239+
case 2:
240+
return String.format("(%s)iter.read(\"%s\", %s.class, %s.class, %s.class);",
241+
clazz.getName(), TypeLiteral.generateCacheKey(type),
242+
clazz.getName(), ((Class) args[0]).getName(), ((Class) args[1]).getName());
243+
default:
244+
throw new IllegalArgumentException("unsupported number of type arguments: " + type);
245+
}
246+
}
247+
throw new IllegalArgumentException("unsupported type: " + type);
187248
}
188249

189250
private static String genArray(Class clazz) {
190251
Class compType = clazz.getComponentType();
191252
if (compType.isArray()) {
192253
throw new IllegalArgumentException("nested array not supported: " + clazz.getCanonicalName());
193254
}
194-
String nativeRead = NATIVE_READS.get(compType.getName());
195255
StringBuilder lines = new StringBuilder();
196256
append(lines, "public Object decode(java.lang.reflect.Type type, com.github.jsoniter.Jsoniter iter) {");
197257
append(lines, "if (!iter.readArray()) {");
198258
append(lines, "return new {{comp}}[0];");
199259
append(lines, "}");
200-
append(lines, "{{comp}} a1 = ({{comp}}) {{op}};");
260+
append(lines, "{{comp}} a1 = {{op}};");
201261
append(lines, "if (!iter.readArray()) {");
202262
append(lines, "return new {{comp}}[]{ a1 };");
203263
append(lines, "}");
204-
append(lines, "{{comp}} a2 = ({{comp}}) {{op}};");
264+
append(lines, "{{comp}} a2 = {{op}};");
205265
append(lines, "if (!iter.readArray()) {");
206266
append(lines, "return new {{comp}}[]{ a1, a2 };");
207267
append(lines, "}");
208-
append(lines, "{{comp}} a3 = ({{comp}}) {{op}};");
268+
append(lines, "{{comp}} a3 = {{op}};");
209269
append(lines, "if (!iter.readArray()) {");
210270
append(lines, "return new {{comp}}[]{ a1, a2, a3 };");
211271
append(lines, "}");
@@ -222,119 +282,103 @@ private static String genArray(Class clazz) {
222282
append(lines, "System.arraycopy(arr, 0, newArr, 0, arr.length);");
223283
append(lines, "arr = newArr;");
224284
append(lines, "}");
225-
append(lines, "arr[i++] = ({{comp}}) {{op}};");
285+
append(lines, "arr[i++] = {{op}};");
226286
append(lines, "}");
227287
append(lines, "{{comp}}[] result = new {{comp}}[i];");
228288
append(lines, "System.arraycopy(arr, 0, result, 0, i);");
229289
append(lines, "return result;");
230290
append(lines, "}");
231-
String op = String.format("iter.read(%s.class)", compType.getCanonicalName());
232-
if (nativeRead != null) {
233-
op = nativeRead;
234-
}
235291
return lines.toString().replace(
236292
"{{comp}}", compType.getCanonicalName()).replace(
237-
"{{op}}", op);
293+
"{{op}}", genReadOp(compType));
238294
}
239295

240-
private static String genCollectionWithCapacity(Class clazz, Class compType) {
241-
String nativeRead = NATIVE_READS.get(compType.getName());
296+
private static String genCollectionWithCapacity(Class clazz, Type compType) {
242297
StringBuilder lines = new StringBuilder();
243298
append(lines, "public Object decode(java.lang.reflect.Type type, com.github.jsoniter.Jsoniter iter) {");
244299
append(lines, "if (!iter.readArray()) {");
245300
append(lines, "return new {{clazz}}(0);");
246301
append(lines, "}");
247-
append(lines, "{{comp}} a1 = ({{comp}}) {{op}};");
302+
append(lines, "Object a1 = {{op}};");
248303
append(lines, "if (!iter.readArray()) {");
249304
append(lines, "{{clazz}} obj = new {{clazz}}(1);");
250305
append(lines, "obj.add(a1);");
251306
append(lines, "return obj;");
252307
append(lines, "}");
253-
append(lines, "{{comp}} a2 = ({{comp}}) {{op}};");
308+
append(lines, "Object a2 = {{op}};");
254309
append(lines, "if (!iter.readArray()) {");
255310
append(lines, "{{clazz}} obj = new {{clazz}}(2);");
256311
append(lines, "obj.add(a1);");
257312
append(lines, "obj.add(a2);");
258313
append(lines, "return obj;");
259314
append(lines, "}");
260-
append(lines, "{{comp}} a3 = ({{comp}}) {{op}};");
315+
append(lines, "Object a3 = {{op}};");
261316
append(lines, "if (!iter.readArray()) {");
262317
append(lines, "{{clazz}} obj = new {{clazz}}(3);");
263318
append(lines, "obj.add(a1);");
264319
append(lines, "obj.add(a2);");
265320
append(lines, "obj.add(a3);");
266321
append(lines, "return obj;");
267322
append(lines, "}");
268-
append(lines, "{{comp}} a4 = ({{comp}}) {{op}};");
323+
append(lines, "Object a4 = {{op}};");
269324
append(lines, "{{clazz}} obj = new {{clazz}}(8);");
270325
append(lines, "obj.add(a1);");
271326
append(lines, "obj.add(a2);");
272327
append(lines, "obj.add(a3);");
273328
append(lines, "obj.add(a4);");
274329
append(lines, "int i = 4;");
275330
append(lines, "while (iter.readArray()) {");
276-
append(lines, "obj.add(({{comp}}) {{op}});");
331+
append(lines, "obj.add({{op}});");
277332
append(lines, "}");
278333
append(lines, "return obj;");
279334
append(lines, "}");
280-
String op = String.format("iter.read(%s.class)", compType.getCanonicalName());
281-
if (nativeRead != null) {
282-
op = nativeRead;
283-
}
284335
return lines.toString().replace(
285336
"{{clazz}}", clazz.getName()).replace(
286-
"{{comp}}", compType.getCanonicalName()).replace(
287-
"{{op}}", op);
337+
"{{op}}", genReadOp(compType));
288338
}
289339

290-
private static String genCollection(Class clazz, Class compType) {
291-
String nativeRead = NATIVE_READS.get(compType.getName());
340+
private static String genCollection(Class clazz, Type compType) {
292341
StringBuilder lines = new StringBuilder();
293342
append(lines, "public Object decode(java.lang.reflect.Type type, com.github.jsoniter.Jsoniter iter) {");
294343
append(lines, "if (!iter.readArray()) {");
295344
append(lines, "return new {{clazz}}();");
296345
append(lines, "}");
297-
append(lines, "{{comp}} a1 = ({{comp}}) {{op}};");
346+
append(lines, "Object a1 = {{op}};");
298347
append(lines, "if (!iter.readArray()) {");
299348
append(lines, "{{clazz}} obj = new {{clazz}}();");
300349
append(lines, "obj.add(a1);");
301350
append(lines, "return obj;");
302351
append(lines, "}");
303-
append(lines, "{{comp}} a2 = ({{comp}}) {{op}};");
352+
append(lines, "Object a2 = {{op}};");
304353
append(lines, "if (!iter.readArray()) {");
305354
append(lines, "{{clazz}} obj = new {{clazz}}();");
306355
append(lines, "obj.add(a1);");
307356
append(lines, "obj.add(a2);");
308357
append(lines, "return obj;");
309358
append(lines, "}");
310-
append(lines, "{{comp}} a3 = ({{comp}}) {{op}};");
359+
append(lines, "Object a3 = {{op}};");
311360
append(lines, "if (!iter.readArray()) {");
312361
append(lines, "{{clazz}} obj = new {{clazz}}();");
313362
append(lines, "obj.add(a1);");
314363
append(lines, "obj.add(a2);");
315364
append(lines, "obj.add(a3);");
316365
append(lines, "return obj;");
317366
append(lines, "}");
318-
append(lines, "{{comp}} a4 = ({{comp}}) {{op}};");
367+
append(lines, "Object a4 = {{op}};");
319368
append(lines, "{{clazz}} obj = new {{clazz}}();");
320369
append(lines, "obj.add(a1);");
321370
append(lines, "obj.add(a2);");
322371
append(lines, "obj.add(a3);");
323372
append(lines, "obj.add(a4);");
324373
append(lines, "int i = 4;");
325374
append(lines, "while (iter.readArray()) {");
326-
append(lines, "obj.add(({{comp}}) {{op}});");
375+
append(lines, "obj.add({{op}});");
327376
append(lines, "}");
328377
append(lines, "return obj;");
329378
append(lines, "}");
330-
String op = String.format("iter.read(%s.class)", compType.getCanonicalName());
331-
if (nativeRead != null) {
332-
op = nativeRead;
333-
}
334379
return lines.toString().replace(
335380
"{{clazz}}", clazz.getName()).replace(
336-
"{{comp}}", compType.getCanonicalName()).replace(
337-
"{{op}}", op);
381+
"{{op}}", genReadOp(compType));
338382
}
339383

340384
private static void append(StringBuilder lines, String str) {

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -664,20 +664,24 @@ public final double readDouble() throws IOException {
664664
}
665665

666666
public final <T> T read(Class<T> clazz) throws IOException {
667-
return (T) Codegen.getDecoder(TypeLiteral.generateCacheKey(clazz), clazz, null).decode(clazz, this);
667+
return (T) Codegen.getDecoder(TypeLiteral.generateCacheKey(clazz), clazz).decode(clazz, this);
668+
}
669+
670+
public final <T> T read(TypeLiteral<T> typeLiteral) throws IOException {
671+
Type type = typeLiteral.getType();
672+
return (T) Codegen.getDecoder(typeLiteral.cacheKey, type).decode(type, this);
668673
}
669674

670675
public final <T> T read(String cacheKey, Class<T> clazz) throws IOException {
671-
return (T) Codegen.getDecoder(cacheKey, clazz, null).decode(clazz, this);
676+
return (T) Codegen.getDecoder(cacheKey, clazz).decode(clazz, this);
672677
}
673678

674-
public final <T> T read(String cacheKey, Class<T> clazz, Type typeArg) throws IOException {
675-
return (T) Codegen.getDecoder(cacheKey, clazz, new Type[]{typeArg}).decode(clazz, this);
679+
public final <T> T read(String cacheKey, Class<T> clazz, Type typeArg1) throws IOException {
680+
return (T) Codegen.getDecoder(cacheKey, clazz, typeArg1).decode(clazz, this);
676681
}
677682

678-
public final <T> T read(TypeLiteral<T> typeLiteral) throws IOException {
679-
Type type = typeLiteral.getType();
680-
return (T) Codegen.getDecoder(typeLiteral.cacheKey, type, null).decode(type, this);
683+
public final <T> T read(String cacheKey, Class<T> clazz, Type typeArg1, Type typeArg2) throws IOException {
684+
return (T) Codegen.getDecoder(cacheKey, clazz, typeArg1, typeArg2).decode(clazz, this);
681685
}
682686

683687
public final void skip() throws IOException {

0 commit comments

Comments
 (0)