启用默认输入后,有没有办法覆盖 Jackson 输出的类型?
With default typing enabled, is there a way to override the type Jackson outputs?
我正在序列化一个 class,其中包含一个启用了默认类型的不可修改的列表。问题是杰克逊使用的类型是
java.util.Collections$UnmodifiableRandomAccessList
由于某种原因,反序列化器不知道如何处理。
有没有办法告诉 Jackson 将类型设置为
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 文档中的安全风险警告开始:
Notes on security: use "default typing" feature (see
enableDefaultTyping()
) is a potential security risk, if used with
untrusted content (content generated by untrusted external parties).
If so, you may want to construct a custom TypeResolverBuilder
implementation to limit possible types to instantiate, (using
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" ] ]
}
我正在序列化一个 class,其中包含一个启用了默认类型的不可修改的列表。问题是杰克逊使用的类型是
java.util.Collections$UnmodifiableRandomAccessList
由于某种原因,反序列化器不知道如何处理。
有没有办法告诉 Jackson 将类型设置为
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 文档中的安全风险警告开始:
Notes on security: use "default typing" feature (see
enableDefaultTyping()
) is a potential security risk, if used with untrusted content (content generated by untrusted external parties). If so, you may want to construct a customTypeResolverBuilder
implementation to limit possible types to instantiate, (usingsetDefaultTyping(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" ] ]
}