java - 启用默认输入后,是否有办法覆盖 Jackson 输出的类型?

标签 java json serialization jackson unmodifiable

我正在序列化一个类,其中包含启用默认类型的不可修改列表。问题是 Jackson 使用的类型是

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接口(interface):

notValid2ValidIds.put("java.util.Collections$UnmodifiableRandomAccessList", List.class.getName());

输出将是:

{
  "data" : [ "java.util.List", [ "1", "b" ] ]
}

关于java - 启用默认输入后,是否有办法覆盖 Jackson 输出的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57597256/

相关文章:

java - 为什么 JFormattedTextField 不支持 actionPerformed

java - 从 JAVA 套接字读取比 Python 慢

php - 如何按照 Apigility 方式验证嵌套数据?

c# - 以编程方式将类序列化为 xsd

使用 Apache Avro 的 java.util.set 字段序列化/反序列化

Java.Lang.VerifyError,Jenkins 错误?

java - 方法specialStateTransition(int, IntStream)的代码超过65535字节

json - Angular 资源将一维字符串数组解析为 2d

json - JSON Jackson 库是否具有 JSON 清理功能?

c# - 为 MongoDb 处理可为空和不可为空类型的自定义序列化程序