Динамическая обработка полиморфного типа с Джексоном - PullRequest
13 голосов
/ 21 ноября 2011

У меня есть иерархия классов, похожая на эту:

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

Я анализирую JSONJava с Джексоном вот так

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

Я хочу десериализовать классы A, B и другие из JSON, используя JAVA / Jackson.В JSON нет информации о типах (и не может быть).Я не могу использовать аннотации к классам (я не являюсь их владельцем), и я (полагаю), я не могу использовать миксины, поскольку существует потенциально произвольное количество классов, таких как A & B (и миксины не являются динамическими).Хорошо, что код десериализации знает, какой правильный пользовательский класс использовать для десериализации (в основном, существует известное сопоставление класса с классом конфигурации), но я не знаю, как заставить Джексона распознавать эту информацию при десериализации JSON.

Вкратце: я хочу иметь возможность разрешать тип десериализации объекта конфигурации в зависимости от типа окружающего класса, устанавливая все, что необходимо для ObjectMapper.Как этого достичь?

Ответы [ 4 ]

5 голосов
/ 24 ноября 2011

Очевидно, что ответом было реализовать что-то похожее на шестое решение, опубликованное в http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html,, которое использует уникальные имена элементов JSON для идентификации целевого типа для десериализации.

0 голосов
/ 24 января 2017

Над ответами изображено решение, однако отсутствует то, что означают фактически используемые аннотации.Если вам интересно, что на самом деле делают эти аннотации, что стоит за ними и почему они необходимы, перейдите по ссылке ниже.Это очень хорошо объяснено.http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

0 голосов
/ 29 декабря 2015

Мне пришлось сделать что-то подобное, и в итоге я создал универсальный сериализатор полиморфных списков и десериализатор.Вот десериализация, которая, я думаю, будет работать для вас:

    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 голосов
/ 17 января 2014

Хороший ответ от программиста Брюса!

У меня есть случай полиморфизма, в котором я хочу сохранить доменные объекты как POJO и не использовать зависимости от аннотаций Джексона.

Поэтому я предпочитаю использовать собственный десериализатор и Фабрику для определения типа или создания конкретных классов.

Вот мой код ... (учтите, что у меня есть иерархия аннотаций, которые на самом деле являются «пользовательскими тегами», а не аннотациями Java)

Вот метод десериализации

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);
}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...