1. 背景
在使用Jackson序列化一个List对象的时候,偶然发现输出的元素都不带表示类型的type字段。先前这个List作为一个JavaBean的字段,序列化整个Bean时,是带有类型的,但单独序列化List的时候,却不带type,造成反序列化无法找到对应的类型。
构造下面的示例以说明问题。
public class Test
{
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME, // Were binding by providing a name
include = JsonTypeInfo.As.PROPERTY, // The name is provided in a property
property = "type", // Property name is type
visible = true // Retain the value of type after deserialisation
)
@JsonSubTypes({//Below, we define the names and the binding classes.
@JsonSubTypes.Type(value = Bean.class, name = "Bean") ,
@JsonSubTypes.Type(value = Bean0.class, name = "Bean0")
})
static class Bean
{
}
static class Bean0 extends Bean
{
}
static class A
{
List<Bean> list = CS.arrayList() ;
public List<Bean> getList()
{
return list;
}
public void setList(List<Bean> aList)
{
list = aList;
}
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, NoSuchFieldException
{
List<Bean> list = CS.arrayList() ;
list.add(new Bean()) ;
list.add(new Bean0()) ;
JacksonUtils.toString(list) ;
System.out.println("List的输出结果:");
System.out.println(JacksonUtils.toString(list));
A a = new A() ;
a.setList(list) ;
System.out.println("类A的输出结果:");
System.out.println(JacksonUtils.toString(a));
}
}
输出结果:
List的输出结果:
[{},{}]
类A的输出结果:
{"list":[{"type":"Bean"},{"type":"Bean0"}]}
2. 原因分析
之所以会如此,这和Java的泛型类型擦除机制有关。在一个方法内部声明的List,如下:
List<Bean> list = CS.arrayList() ;
list.add(new Bean()) ;
list.add(new Bean0()) ;
通过反射机制,是无法得知list里面的元素类型是Bean的,因为这里List<Bean>
指定的Bean类型会被擦除,对于运行期来说,和List<Object>
没有区别。
对于类型A里面的List<Bean> list ;
字段来说,它是由额外的记录的,取得方法如下:
ParameterizedType type = (ParameterizedType)A.class.getDeclaredField("list").getGenericType();
System.out.println(type.getRawType());
System.out.println(type.getActualTypeArguments()[0]);
输出结果:
interface java.util.List
class com.cimstech.st.ms.ia.Test$Bean
因为类型被擦除了,所以它在反序列化的时候,不能得知List里面元素的基准类型,它就和List<Object>
没有区别,从而无法同步基准类型寻找到@JsonTypeInfo
和@JsonSubTypes
注解上的信息,只能把里面的元素当作JavaBean序列化。
3. 解决办法
List<Bean> list = CS.arrayList() ;
list.add(new Bean()) ;
list.add(new Bean0()) ;
System.out.println("List的输出结果:") ;
System.out.println(JacksonUtils.toString(list , new TypeReference<List<Bean>>(){}));
输出结果:
List的输出结果:
[{"type":"Bean"},{"type":"Bean0"}]
JacksonUtils的toString方法:
public static <T> String toString(Object aBean , TypeReference<T> aTypeReference)
{
if(aBean == null)
return null ;
try
{
return getObjectMapper().writerFor(aTypeReference).writeValueAsString(aBean) ;
}
catch (JsonProcessingException e)
{
throw new JSONException(e) ;
}
}