Десериализовать ключ WRAPPER_OBJECT как свойство, используя Джексона - PullRequest
1 голос
/ 06 октября 2019

Я пытаюсь десериализовать эти данные json в список объектов:

[{
  "a": {
    "commonField": 1,
    "aField": "AAA"
  }
}, {
  "b": {
    "commonField": 2,
    "bField": "BBB"
  }
}]

Каждый объект может быть одного из нескольких типов, имеющих как общие, так и уникальные поля. Информация о точной форме объекта хранится в json как ключ к объекту-оболочке.

Я создал соответствующие классы для каждой известной формы (набор уникальных полей), расширяя класс, содержащий все общие поля. Кроме того, я добавил аннотации Джексона в классы, чтобы включить полиморфную десериализацию. Упрощенные результирующие классы выглядят так:

@JsonTypeInfo(use = Id.NAME, include = As.WRAPPER_OBJECT, property = "type")
@JsonSubTypes({
  @JsonSubTypes.Type(KeyBasedSubTypeA.class),
  @JsonSubTypes.Type(KeyBasedSubTypeB.class)
})
public abstract class KeyBasedSuperType {
  public String type;
  public int commonField;
}

@JsonTypeName("a")
public class KeyBasedSubTypeA extends KeyBasedSuperType {
  public String aField;
}

@JsonTypeName("b")
public class KeyBasedSubTypeB extends KeyBasedSuperType {
  public String bField;
}

С этой настройкой Джексон отлично работает почти . Он может выбрать правильный подтип во время десериализации и заполнить все поля, включая общие и уникальные. Однако поле type не обновляется Джексоном, значение ключа, использованное для выбора подтипа, нигде не сохраняется. Другими словами, данные десериализуются в следующую структуру:

[KeyBasedSubTypeA { type=null; commonField=1; aField=AAA },
 KeyBasedSubTypeB { type=null; commonField=2; bField=BBB }]

Примечание type поле, имеющее нулевое значение. Итак, вопрос - Как я могу заставить Джексона хранить ключ оболочки, используемый для выбора подтипа где-то в результирующем объекте?

Вот мой тест JUnit для процесса

public class PolymorphicTest {
  private static ObjectMapper mapper;

  @BeforeClass
  public static void init() {
    mapper = new ObjectMapper();
  }

  @Test
  public void testKeyDenominator() throws IOException {
    TypeReference<List<KeyBasedSuperType>> dataShape =
        new TypeReference<List<KeyBasedSuperType>>() {};
    List<KeyBasedSuperType> result = mapper.readValue(
        PolymorphicTest.class.getResourceAsStream("polymorphic-key.json"), dataShape);
    assertEquals(2, result.size());
    assertEquals(KeyBasedSubTypeA.class, result.get(0).getClass());
    assertEquals(KeyBasedSubTypeB.class, result.get(1).getClass());
    assertEquals(1, result.get(0).commonField);
    assertEquals(2, result.get(1).commonField);
    assertEquals("a", result.get(0).type); // <---- this line fails
    assertEquals("b", result.get(1).type); // <---- this line fails
    assertEquals("AAA", ((KeyBasedSubTypeA) result.get(0)).aField);
    assertEquals("BBB", ((KeyBasedSubTypeB) result.get(1)).bField);
  }

}

1 Ответ

1 голос
/ 07 октября 2019

Решение на самом деле было очень близко, просто пропустил крошечный шаг вперед. @JsonTypeInfo(visible=true) требуется, чтобы Джексон обрабатывал информацию о типе как обычное свойство.

@JsonTypeInfo(use = Id.NAME, include = As.WRAPPER_OBJECT, property = "type", visible = true)
@JsonSubTypes({
  @JsonSubTypes.Type(KeyBasedSubTypeA.class),
  @JsonSubTypes.Type(KeyBasedSubTypeB.class)
})
public abstract class KeyBasedSuperType {
  public String type;
  public int commonField;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...