Dynamic polymorphic type handling with Jackson

后端 未结 4 1068
暗喜
暗喜 2020-12-31 07:40

I have a class hierarchy similar to this one:

public static class BaseConfiguration {
}

public abstract class Base {
  private BaseConfiguration configurati         


        
相关标签:
4条回答
  • 2020-12-31 08:10

    Good answer provided by Programmer Bruce!

    I have a case of polymorphism in which I want to keep the domain objects as POJOs and not use dependencies on Jackson annotations.

    Therefore I preffer to use a custom deserializer and a Factory for decising the type or intantiating the concrete classes.

    Here is my code ... (be aware that I have an Annotation Hierarchy which are in fact "User Tags" and not Java Annotations )

    Here is the deserialization Method

    public class AnnotationDeserializer extends StdDeserializer<Annotation> {
    
    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<? extends Annotation> realClass = null;
    
        Iterator<Entry<String, JsonNode>> elementsIterator = root.getFields();
        while (elementsIterator.hasNext()) {
            Entry<String, JsonNode> 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);
    }
    }
    
    0 讨论(0)
  • 2020-12-31 08:13

    Apparently the answer was to implement something similar to the sixth solution posted at http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html, which uses unique JSON element names to identify the target type to deserialize to.

    0 讨论(0)
  • 2020-12-31 08:16

    I had to do something similar, and ended up creating a generic polymorphic list serializer and deserialzer. Here is the deserialize that I think will work for you:

        public class PolymorphicListDeserializer extends JsonDeserializer<List<?>> implements ContextualDeserializer {
      private HashMap<String, Class> _typeMap = null;
    
      private Class _elementType;
    
      private static <T> List<T> getNewList(Class<T> clazz) {
        return new ArrayList<T>();
      }
    
      @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<List<?>> 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<String, Class>();
        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;
      }
    
    0 讨论(0)
  • 2020-12-31 08:26

    Above answers depicts a solution however lack what actually used annotations mean. If you are curious about what actually these annotation do, idea behind them & why they are required please go through the below link. Its explained very nicely in it. https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization

    0 讨论(0)
提交回复
热议问题