对于使用Jackson实现Collection的类,反序列化失败

Vin*_*rat 10 java json jackson

我有以下JSON:

{
  "item": [
    { "foo": 1 },
    { "foo": 2 }
  ]
} 
Run Code Online (Sandbox Code Playgroud)

这基本上是一个包含项集合的对象.

所以我做了一个课程来反序列化:

public class ItemList {
  @JsonProperty("item")
  List<Item> items;

  // Getters, setters & co.
  // ...
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,一切都很顺利.

现在,为了让我的生活更轻松,我决定能够迭代ItemList对象并让它实现Collection接口.

基本上我的班级成了:

public class ItemList implements Collection<Item>, Iterable<Item> {
  @JsonProperty("item")
  List<Item> items;

  // Getters, setters & co.

  // Generated all method delegates to items. For instance:
  public Item get(int position) {
    return items.get(position);
  }
}
Run Code Online (Sandbox Code Playgroud)

该实现工作正常,很好.但是,反序列化现在失败了.

杰克逊看起来很困惑:

com.fasterxml.jackson.databind.JsonMappingException:无法从START_OBJECT标记中反序列化com.example.ItemList的实例

我试图添加,@JsonDeserialize(as=ItemList.class)但它没有做到这一点.

怎么走?

Ale*_*lov 5

显然它不起作用,因为杰克逊对 Java 集合类型使用标准集合反序列化器,它对ItemList属性一无所知。

可以让它工作,但不是以一种非常优雅的方式。您需要配置ObjectMapper以替换为相应类型手动创建的 bean 反序列化器上的默认集合反序列化器。我已经编写了一个示例,该示例BeanDeserializerModifier对使用自定义注释进行注释的所有类执行此操作。

请注意,我要重写ObjectMapper以访问受保护方法createDeserializationContextObjectMapper创建合适的deserialisation上下文因为bean的修改不能访问它。

这是代码:

public class JacksonCustomList {
    public static final String JSON = "{\n" +
            "  \"item\": [\n" +
            "    { \"foo\": 1 },\n" +
            "    { \"foo\": 2 }\n" +
            "  ]\n" +
            "} ";

    @Retention(RetentionPolicy.RUNTIME)
    public static @interface PreferBeanDeserializer {

    }

    public static class Item {
        public int foo;

        @Override
        public String toString() {
            return String.valueOf(foo);
        }
    }

    @PreferBeanDeserializer
    public static class ItemList extends ArrayList<Item> {
        @JsonProperty("item")
        public List<Item> items;

        @Override
        public String toString() {
            return items.toString();
        }
    }

    public static class Modifier extends BeanDeserializerModifier {
        private final MyObjectMapper mapper;

        public Modifier(final MyObjectMapper mapper) {
            this.mapper = mapper;
        }

        @Override
        public JsonDeserializer<?> modifyCollectionDeserializer(
                final DeserializationConfig config,
                final CollectionType type,
                final BeanDescription beanDesc,
                final JsonDeserializer<?> deserializer) {
            if (type.getRawClass().getAnnotation(PreferBeanDeserializer.class) != null) {
                DeserializationContext context = mapper.createContext(config);
                try {
                    return context.getFactory().createBeanDeserializer(context, type, beanDesc);
                } catch (JsonMappingException e) {
                   throw new IllegalStateException(e);
                }

            }
            return super.modifyCollectionDeserializer(config, type, beanDesc, deserializer);
        }
    }

    public static class MyObjectMapper extends ObjectMapper {
        public DeserializationContext createContext(final DeserializationConfig cfg) {
            return super.createDeserializationContext(getDeserializationContext().getParser(), cfg);
        }
    }

    public static void main(String[] args) throws IOException {
        final MyObjectMapper mapper = new MyObjectMapper();
        SimpleModule module = new SimpleModule();
        module.setDeserializerModifier(new Modifier(mapper));

        mapper.registerModule(module);
        System.out.println(mapper.readValue(JSON, ItemList.class));
    }

}
Run Code Online (Sandbox Code Playgroud)