我有以下JSON:
{
"item": [
{ "foo": 1 },
{ "foo": 2 }
]
}
这基本上是一个包含项目集合的对象。
所以我做了一个课程来反序化:
public class ItemList {
@JsonProperty("item")
List<Item> items;
// Getters, setters & co.
// ...
}
到目前为止,一切都很顺利。
现在,为了让我的生活更轻松,我决定能够迭代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);
}
}
实施工作正常而且很好。但是,反序列化现在失败了。
杰克逊看起来很困惑:
com.fasterxml.jackson.databind.JsonMappingException:不能 从START_OBJECT标记
中反序列化com.example.ItemList的实例
我试图添加@JsonDeserialize(as=ItemList.class)
,但它没有做到这一点。
要走的路是什么?
答案 0 :(得分:4)
显然它不起作用,因为Jackson对Java集合类型使用标准集合反序列化器,它对ItemList
属性一无所知。
可以使它工作但不是非常优雅。您需要配置ObjectMapper
以替换为相应类型手动创建的bean反序列化器上的默认集合反序列化器。我已经在BeanDeserializerModifier
中为所有使用自定义注释注释的类编写了一个示例。
请注意,我必须重写ObjectMapper
才能访问createDeserializationContext
的受保护方法ObjectMapper
以创建正确的反序列化上下文,因为bean修饰符无法访问它。 / p>
以下是代码:
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));
}
}
答案 1 :(得分:2)
如果您认为item
属性为根值,则可以使用ItemList
annotation更改您的@JsonRootName
课程,如下所示:
@JsonRootName("item")
public class ItemList implements Collection<Item>, Iterable<Item> {
private List<Item> items = new ArrayList<>();
public Item get(int position) {
return items.get(position);
}
// implemented methods deferring to delegate
// ...
}
如果您然后激活UNWRAP_ROOT_VALUE
deserialization feature,事情就会按预期运行:
String json = "{\"item\": [{\"foo\": 1}, {\"foo\": 2}]}";
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.reader(ItemList.class);
ItemList itemList = reader
.with(DeserializationFeature.UNWRAP_ROOT_VALUE)
.readValue(json);
序列化同样有效,启用了WRAP_ROOT_VALUE
serialization feature:
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writer();
Item item1 = new Item();
item1.setFoo(1);
Item item2 = new Item();
item2.setFoo(2);
ItemList itemList = new ItemList();
itemList.add(item1);
itemList.add(item2);
String json = writer
.with(SerializationFeature.WRAP_ROOT_VALUE)
.writeValueAsString(itemList);
// json contains {"item":[{"foo":1},{"foo":2}]}
如果您的ItemList
包含还需要序列化/反序列化的其他属性(实际列表除外),此解决方案显然是不够的。