Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2960,8 +2960,20 @@ public <T> FieldReader createFieldReaderMethod(

return new FieldReaderList(fieldName, fieldTypeResolved, fieldClassResolved, itemType, itemClass, ordinal, features, format, locale, null, jsonSchema, method, null, null);
}
} else {
Type itemType = BeanUtils.resolveCollectionItemType(fieldTypeResolved, fieldClass);
Class itemClass;
if (itemType == null) {
itemType = Object.class;
itemClass = Object.class;
} else {
itemClass = TypeUtils.getMapping(itemType);
if (itemClass == String.class) {
return new FieldReaderList(fieldName, fieldTypeResolved != null ? fieldTypeResolved : fieldType, fieldClassResolved != null ? fieldClassResolved : fieldClass, String.class, String.class, ordinal, features, format, locale, null, jsonSchema, method, null, null);
}
}
return new FieldReaderList(fieldName, fieldTypeResolved != null ? fieldTypeResolved : fieldType, fieldClassResolved != null ? fieldClassResolved : fieldClass, itemType, itemClass, ordinal, features, format, locale, null, jsonSchema, method, null, null);
}
return new FieldReaderList(fieldName, fieldType, fieldClass, Object.class, Object.class, ordinal, features, format, locale, null, jsonSchema, method, null, null);
}

if (fieldClass == Date.class) {
Expand Down Expand Up @@ -3367,6 +3379,8 @@ public <T> FieldReader<T> createFieldReader(
if (actualTypeArguments.length > 0) {
itemType = actualTypeArguments[0];
}
} else {
itemType = BeanUtils.resolveCollectionItemType(fieldTypeResolved, fieldClass);
}
if (itemType == null) {
itemType = Object.class;
Expand Down
22 changes: 22 additions & 0 deletions core/src/main/java/com/alibaba/fastjson2/util/BeanUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,28 @@ public static Field getDeclaredField(Class objectClass, String fieldName) {
return fieldMap.get(fieldName);
}

public static Type resolveCollectionItemType(Type fieldTypeResolved, Class<?> fieldClass) {
Type contextType = fieldTypeResolved != null ? fieldTypeResolved : fieldClass;

Type listType = getGenericSupertype(contextType, fieldClass, List.class);

if (listType == null || listType == List.class) {
listType = getGenericSupertype(contextType, fieldClass, Collection.class);
}

listType = BeanUtils.resolve(contextType, fieldClass, listType);

if (listType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) listType;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

if (actualTypeArguments.length == 1) {
return actualTypeArguments[0];
}
}
return null;
}

public static Method getSetter(Class objectClass, String methodName) {
Method[] methods = new Method[1];
setters(objectClass, e -> {
Expand Down
171 changes: 171 additions & 0 deletions core/src/test/java/com/alibaba/fastjson2/issues_3900/Issue3926.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package com.alibaba.fastjson2.issues_3900;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class Issue3926 {
public static class MyDTO1 {
private MyList myList;

public MyList getMyList() {
return myList;
}

public void setMyList(MyList myList) {
this.myList = myList;
}
}

public static class MyList extends ArrayList<MyObject1> {
}

public static class MyObject1 {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

@Test
public void testCustomList() {
String json = "{\"myList\":[{\"name\":\"张三\"}]}";
MyDTO1 myDTO = JSON.parseObject(json, MyDTO1.class);

assertEquals(1, myDTO.getMyList().size());

Object obj = myDTO.getMyList().get(0);
assertTrue(obj != null, "Element should be MyObject, but is " + obj.getClass().getName());

MyObject1 myObject = myDTO.getMyList().get(0);
assertEquals("张三", myObject.getName());
}

@Test
public void testCustomListMultiple() {
String json = "{\"myList\":[{\"name\":\"张三\"},{\"name\":\"李四\"}]}";
MyDTO1 myDTO = JSON.parseObject(json, MyDTO1.class);

assertEquals(2, myDTO.getMyList().size());

for (int i = 0; i < myDTO.getMyList().size(); i++) {
Object obj = myDTO.getMyList().get(i);
assertTrue(obj != null, "Element " + i + " should be MyObject, but is " + obj.getClass().getName());
}

MyObject1 myObject = myDTO.getMyList().get(0);
assertEquals("张三", myObject.getName());

MyObject1 myObject2 = myDTO.getMyList().get(1);
assertEquals("李四", myObject2.getName());
}

@Test
public void testFieldBasedList() {
String json = "{\"myList\":[{\"name\":\"张三\"}]}";
MyDTO1 myDTO = JSON.parseObject(json, MyDTO1.class, JSONReader.Feature.FieldBased);
assertEquals(1, myDTO.myList.size());
MyObject1 myObject = myDTO.myList.get(0);
assertEquals("张三", myObject.name);
}

@Test
public void testFieldBasedListMultiple() {
String json = "{\"myList\":[{\"name\":\"张三\"},{\"name\":\"李四\"}]}";
MyDTO1 myDTO = JSON.parseObject(json, MyDTO1.class, JSONReader.Feature.FieldBased);
assertEquals(2, myDTO.myList.size());
assertEquals("张三", myDTO.myList.get(0).name);
assertEquals("李四", myDTO.myList.get(1).name);
}

public static class MyDTO2 {
private MyList2 myList;

public MyList2 getMyList() {
return myList;
}

public void setMyList(MyList2 myList) {
this.myList = myList;
}
}

public interface MyListInterface extends java.util.List<MyObject2> {
}

public static class MyList2 extends ArrayList<MyObject2> implements MyListInterface {
}

public static class MyObject2 {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

@Test
public void testCustomListWithInterface() {
String json = "{\"myList\":[{\"name\":\"张三\"}]}";
MyDTO2 myDTO = JSON.parseObject(json, MyDTO2.class);

assertEquals(1, myDTO.getMyList().size());

Object obj = myDTO.getMyList().get(0);
assertTrue(obj != null, "Element should be MyObject2, but is " + obj.getClass().getName());

MyObject2 myObject = myDTO.getMyList().get(0);
assertEquals("张三", myObject.getName());
}

@Test
public void testCustomListWithInterfaceMultiple() {
String json = "{\"myList\":[{\"name\":\"张三\"},{\"name\":\"李四\"}]}";
MyDTO2 myDTO = JSON.parseObject(json, MyDTO2.class);

assertEquals(2, myDTO.getMyList().size());

for (int i = 0; i < myDTO.getMyList().size(); i++) {
Object obj = myDTO.getMyList().get(i);
assertTrue(obj instanceof MyObject2, "Element " + i + " should be MyObject2, but is " + obj.getClass().getName());
}

MyObject2 myObject = myDTO.getMyList().get(0);
assertEquals("张三", myObject.getName());

MyObject2 myObject2 = myDTO.getMyList().get(1);
assertEquals("李四", myObject2.getName());
}

@Test
public void testFieldBasedInterfaceList() {
String json = "{\"myList\":[{\"name\":\"张三\"}]}";
MyDTO2 myDTO = JSON.parseObject(json, MyDTO2.class, JSONReader.Feature.FieldBased);
assertEquals(1, myDTO.myList.size());
MyObject2 myObject = myDTO.myList.get(0);
assertEquals("张三", myObject.name);
}

@Test
public void testFieldBasedInterfaceListMultiple() {
String json = "{\"myList\":[{\"name\":\"张三\"},{\"name\":\"李四\"}]}";
MyDTO2 myDTO = JSON.parseObject(json, MyDTO2.class, JSONReader.Feature.FieldBased);
assertEquals(2, myDTO.myList.size());
assertEquals("张三", myDTO.myList.get(0).name);
assertEquals("李四", myDTO.myList.get(1).name);
}
}
Loading