Jackson的动态多态类型处理

我有一个类似于这个的类层次结构:

public static class BaseConfiguration { } public abstract class Base { private BaseConfiguration configuration; public String id; public BaseConfiguration getConfiguration() { ... } public void setConfiguration(BaseConfiguration configuration) { ... } } public class A extends Base { public static class CustomConfigurationA extends BaseConfiguration { String filename; String encoding; } CustomConfigurationA getConfiguration() { ... } } class B extends Base { public static class CustomConfigurationB extends BaseConfiguration { /* ... */ } CustomConfigurationB getConfiguration() { ... } } 

和json像这样输入(我不能改变自己)

 { "id":"File1", "configuration":{ "filename":"...", "encoding":"UTF-8" } } 

我正在用jackson解析Java中的JSON

 ObjectMapper mapper = new ObjectMapper(); value = mapper.readValue(in, nodeType); 

我想使用JAVA / Jackson从JSON反序列化A,B和其他类。 JSON中没有嵌入类型信息(也不可能)。 我不能在类上使用注释(我不拥有它们)而我(相信)我不能使用mixin,因为有可能任意数量的类,如A和B(并且mixins不是动态的)。 好的是,反序列化代码知道哪个是用于反序列化的正确自定义类(基本上有从类到配置类的已知映射),但我不知道如何使jackson在反序列化JSON时识别这些信息。

简而言之:我希望能够通过设置ObjectMapper上的必要内容,根据周围的类类型解析配置对象的反序列化类型。 怎么能实现这一目标?

显然答案是实现类似于http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html上发布的第六个解决方案,它使用唯一的JSON元素名称来识别目标要反序列化的类型。

程序员布鲁斯提供了很好的答案!

我有一个多态的情况,我希望将域对象保留为POJO,而不是使用对Jackson注释的依赖。

因此,我倾向于使用自定义反序列化器和工厂来确定类型或实例化具体类。

这是我的代码……(请注意,我有一个注释层次结构,实际上是“用户标记”而不是Java注释)

这是反序列化方法

 public class AnnotationDeserializer extends StdDeserializer { AnnotationDeserializer() { super(Annotation.class); } @Override public Annotation deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { ObjectMapper mapper = (ObjectMapper) jp.getCodec(); ObjectNode root = (ObjectNode) mapper.readTree(jp); Class realClass = null; Iterator> elementsIterator = root.getFields(); while (elementsIterator.hasNext()) { Entry element = elementsIterator.next(); if ("type".equals(element.getKey())) { realClass = AnnotationObjectFactory.getInstance() .getAnnotationClass(element.getKey()); break; } } if (realClass == null) return null; return mapper.readValue(root, realClass); } } 

我必须做类似的事情,最后创建一个通用的多态列表序列化器和反序列化器。 以下是我认为适合您的反序列化:

  public class PolymorphicListDeserializer extends JsonDeserializer> implements ContextualDeserializer { private HashMap _typeMap = null; private Class _elementType; private static  List getNewList(Class clazz) { return new ArrayList(); } @Override public List deserialize(final JsonParser jp, DeserializationContext ctxt) throws IOException { final List list = getNewList(_elementType); JsonToken nextToken = jp.getCurrentToken(); if (nextToken == JsonToken.START_OBJECT) { if ( _typeMap.containsKey( currentFieldName )) { list.add( _elementType.cast( ctxt.readValue( jp, _typeMap.get( currentFieldName ) ) ) ); } nextToken = jp.nextToken(); } else if (currentFieldName != null && isEndToken(nextToken) && wrapperCount == 0) { break; } else { nextToken = jp.nextToken(); } } return list; } public JsonDeserializer> createContextual( DeserializationContext ctxt, BeanProperty property ) throws JsonMappingException { //In Jackson 2.6.3, this method is called once per instance and the exception is never thrown if ( _typeMap == null ) _typeMap = new HashMap(); else throw new RuntimeException("Unsupported version of Jackson. Code assumes context is created once and only once."); _elementType = property.getType().getContentType().getRawClass(); //For now, requiring XmlElements annotation to work. May add support for JsonElements (if needed) later. for (XmlElement e : property.getAnnotation(XmlElements.class).value()) { _typeMap.put(e.name(), e.type()); } return this; } private static boolean isStartToken(JsonToken t) { boolean result = false; if (t == JsonToken.START_OBJECT) { result = true; } else if (t == JsonToken.START_ARRAY) { result = true; } return result; } 

上面的答案描述了一个解决方案,但缺乏实际使用的注释意味着。 如果您对这些注释的实际内容,它们背后的想法以及为什么需要它们感到好奇,请通过以下链接进行操作。 它的解释非常好。 http://wiki.fasterxml.com/JacksonPolymorphicDeserialization