【问题标题】:With default typing enabled, is there a way to override the type Jackson outputs?启用默认类型后,有没有办法覆盖杰克逊输出的类型?
【发布时间】:2019-08-21 18:20:36
【问题描述】:

我正在序列化一个类,该类包含一个启用了默认类型的不可修改列表。问题是杰克逊使用的类型是

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;
    ) {}
}

不幸的是,这不起作用。

【问题讨论】:

    标签: java json serialization jackson unmodifiable


    【解决方案1】:

    我想从来自ObjectMapper 文档的安全风险警告开始:

    安全注意事项:使用“默认输入”功能(请参阅 enableDefaultTyping()) 是一个潜在的安全风险,如果与 不受信任的内容(由不受信任的外部方生成的内容)。 如果是这样,您可能需要构建一个自定义 TypeResolverBuilder 实现来限制可能的类型来实例化,(使用 setDefaultTyping(com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder&lt;?))。

    让我们实现自定义解析器:

    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" ] ]
    }
    

    【讨论】:

    • 感谢您的安全警告。这对我的用例来说不是问题,因为它仅供内部使用。该解决方案只需进行一点小改动。出于某种原因,this._includeAs = JsonTypeInfo.As.PROPERTY 对我不起作用,但 this._includeAs = JsonTypeInfo.As.WRAPPER_ARRAY 确实起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-04
    • 1970-01-01
    相关资源
    最近更新 更多