java - 启用默认类型后,有没有办法覆盖杰克逊输出的类型?
问题描述
我正在序列化一个类,该类包含一个启用了默认类型的不可修改列表。问题是杰克逊使用的类型是
java.util.Collections$UnmodifiableRandomAccessList
由于某种原因,反序列化器不知道如何处理。
有没有办法告诉杰克逊将类型设置为
java.util.ArrayList
反序列化器确实知道如何处理?如果可能的话,我想用 mixins 来做。
就像是
public abstract class ObjectMixin {
@JsonCreator
public ObjectMixin(
@JsonProperty("id") String id,
@JsonProperty("list") @JsonSerialize(as = ArrayList.class) List<String> list;
) {}
}
不幸的是,这不起作用。
解决方案
我想从来自ObjectMapper文档的安全风险警告开始:
安全注意事项:如果与不受信任的内容(由不受信任的外部方生成的内容)一起使用,使用“默认类型”功能(请参阅 参考资料
enableDefaultTyping()
)是一个潜在的安全风险 。如果是这样,您可能想要构建一个自定义 实现来限制可能的类型实例化,(使用 )。TypeResolverBuilder
setDefaultTyping(com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder<?)
让我们实现自定义解析器:
class CollectionsDefaultTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
private final Map<String, String> notValid2ValidIds = new HashMap<>();
public CollectionsDefaultTypeResolverBuilder() {
super(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
this._idType = JsonTypeInfo.Id.CLASS;
this._includeAs = JsonTypeInfo.As.PROPERTY;
notValid2ValidIds.put("java.util.Collections$UnmodifiableRandomAccessList", ArrayList.class.getName());
// add more here...
}
@Override
protected TypeIdResolver idResolver(MapperConfig<?> config, JavaType baseType, Collection<NamedType> subtypes,
boolean forSer, boolean forDeser) {
return new ClassNameIdResolver(baseType, config.getTypeFactory()) {
@Override
protected String _idFrom(Object value, Class<?> cls, TypeFactory typeFactory) {
String id = notValid2ValidIds.get(cls.getName());
if (id != null) {
return id;
}
return super._idFrom(value, cls, typeFactory);
}
};
}
}
现在,我们可以如下使用它:
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JsonApp {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.setDefaultTyping(new CollectionsDefaultTypeResolverBuilder());
Root root = new Root();
root.setData(Collections.unmodifiableList(Arrays.asList("1", "b")));
String json = mapper.writeValueAsString(root);
System.out.println(json);
System.out.println(mapper.readValue(json, Root.class));
}
}
class Root {
private List<String> data;
public List<String> getData() {
return data;
}
public void setData(List<String> data) {
this.data = data;
}
@Override
public String toString() {
return "Root{" +
"data=" + data +
'}';
}
}
上面的代码打印:
{
"data" : [ "java.util.ArrayList", [ "1", "b" ] ]
}
Root{data=[1, b]}
您甚至可以将其映射到List
界面:
notValid2ValidIds.put("java.util.Collections$UnmodifiableRandomAccessList", List.class.getName());
输出将是:
{
"data" : [ "java.util.List", [ "1", "b" ] ]
}
推荐阅读
- maven - 如何将输出的 Jar 和 War 文件放在另一个指定的文件夹中(它可能在项目之外)?
- python - 如何使用 python 从给定坐标创建 3D 表面?
- extjs - 使用 Extjs 的空白数字字段
- amazon-web-services - 在 S3 过滤器前缀中指定通配符
- php - 在服务器中使用锚按钮的问题(cent os)
- android - Xamarin.Android:密钥侦听器不起作用
- powershell - 尝试从 CSV 错误“Move-CsUser:无法绑定参数'Identity'。无法转换值...”中的选择中移动用户
- ocaml - 将 ocamlyacc 与 sedlex 一起使用
- python - 如何根据列表项打破列表并创建相关字符串
- java - 将 Excel 文件 (xlsx) 转换为 Json