fixed: parse empty collection in kotlin (#1540)

Signed-off-by: Kraity <kraty@krait.cn>
This commit is contained in:
Kraity 2023-06-10 12:27:18 +08:00 committed by 温绍锦
parent f25d2e63a3
commit 37c584fbb9
3 changed files with 86 additions and 17 deletions

View File

@ -5,6 +5,7 @@ import com.alibaba.fastjson2.util.Fnv;
import com.alibaba.fastjson2.util.GuavaSupport;
import com.alibaba.fastjson2.util.TypeUtils;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
@ -38,6 +39,7 @@ public final class ObjectReaderImplList
final String itemClassName;
final long itemClassNameHash;
final Function builder;
Object listSingleton;
ObjectReader itemObjectReader;
volatile boolean instanceError;
@ -164,14 +166,31 @@ public final class ObjectReaderImplList
break;
default:
instanceClass = listClass;
break;
}
}
if (type == ObjectReaderImplList.CLASS_EMPTY_SET
|| type == ObjectReaderImplList.CLASS_EMPTY_LIST
) {
return new ObjectReaderImplList(type, (Class) type, (Class) type, Object.class, null);
switch (type.getTypeName()) {
case "kotlin.collections.EmptySet":
case "kotlin.collections.EmptyList": {
Object empty;
Class<?> clazz = (Class<?>) type;
try {
Field field = clazz.getField("INSTANCE");
if (!field.isAccessible()) {
field.setAccessible(true);
}
empty = field.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException("Failed to get singleton of " + type, e);
}
return new ObjectReaderImplList(clazz, empty);
}
case "java.util.Collections$EmptySet": {
return new ObjectReaderImplList((Class) type, Collections.emptySet());
}
case "java.util.Collections$EmptyList": {
return new ObjectReaderImplList((Class) type, Collections.emptyList());
}
}
if (itemType == String.class && builder == null) {
@ -185,6 +204,11 @@ public final class ObjectReaderImplList
return new ObjectReaderImplList(type, listClass, instanceClass, itemType, builder);
}
ObjectReaderImplList(Class listClass, Object listSingleton) {
this(listClass, listClass, listClass, Object.class, null);
this.listSingleton = listSingleton;
}
public ObjectReaderImplList(Type listType, Class listClass, Class instanceType, Type itemType, Function builder) {
this.listType = listType;
this.listClass = listClass;
@ -306,12 +330,8 @@ public final class ObjectReaderImplList
return new TreeSet();
}
if (instanceType == CLASS_EMPTY_LIST) {
return Collections.emptyList();
}
if (instanceType == CLASS_EMPTY_SET) {
return Collections.emptySet();
if (listSingleton != null) {
return listSingleton;
}
if (instanceType != null) {

View File

@ -36,6 +36,7 @@ public final class ObjectReaderImplMap
final Class instanceType;
final long features;
final Function builder;
Object mapSingleton;
volatile boolean instanceError;
public static ObjectReader of(Type fieldType, Class mapType, long features) {
@ -87,9 +88,6 @@ public final class ObjectReaderImplMap
case "java.util.Collections$SynchronizedSortedMap":
instanceType = TreeMap.class;
builder = (Function<SortedMap, SortedMap>) Collections::synchronizedSortedMap;
break;
default:
break;
}
}
@ -137,6 +135,22 @@ public final class ObjectReaderImplMap
builder = GuavaSupport.createConvertFunction(instanceType);
instanceType = HashMap.class;
break;
case "kotlin.collections.EmptyMap": {
Object mapSingleton;
try {
Field field = instanceType.getField("INSTANCE");
if (!field.isAccessible()) {
field.setAccessible(true);
}
mapSingleton = field.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException("Failed to get singleton of " + instanceType, e);
}
return new ObjectReaderImplMap(instanceType, features, mapSingleton);
}
case "java.util.Collections$EmptyMap": {
return new ObjectReaderImplMap(instanceType, features, Collections.EMPTY_MAP);
}
default:
if (instanceType == JSONObject1O.class) {
Class objectClass = CLASS_JSON_OBJECT_1x;
@ -154,12 +168,16 @@ public final class ObjectReaderImplMap
return Collections.singletonMap(entry.getKey(), entry.getValue());
};
}
break;
}
return new ObjectReaderImplMap(fieldType, mapType, instanceType, features, builder);
}
ObjectReaderImplMap(Class mapClass, long features, Object mapSingleton) {
this(mapClass, mapClass, mapClass, features, null);
this.mapSingleton = mapSingleton;
}
ObjectReaderImplMap(Type fieldType, Class mapType, Class instanceType, long features, Function builder) {
this.fieldType = fieldType;
this.mapType = mapType;
@ -193,8 +211,8 @@ public final class ObjectReaderImplMap
return new JSONObject();
}
if (instanceType == CLASS_EMPTY_MAP) {
return Collections.emptyMap();
if (mapSingleton != null) {
return mapSingleton;
}
if (instanceType == CLASS_EMPTY_SORTED_MAP) {

View File

@ -0,0 +1,31 @@
package com.alibaba.fastjson2.issues
import com.alibaba.fastjson2.JSON
import com.alibaba.fastjson2.toJSONString
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertInstanceOf
class Issue1540 {
@Test
fun test_map() {
val map = emptyMap<Any?, Any?>()
assertEquals("{}", map.toJSONString())
assertInstanceOf(map::class.java, JSON.parseObject("{}", map::class.java))
}
@Test
fun test_set() {
val set = emptySet<Any?>()
assertEquals("[]", set.toJSONString())
assertInstanceOf(set::class.java, JSON.parseObject("[]", set::class.java))
}
@Test
fun test_list() {
val list = emptyList<Any?>()
assertEquals("[]", list.toJSONString())
assertInstanceOf(list::class.java, JSON.parseObject("[]", list::class.java))
}
}