Рекурс Коллекция Полиморфи c Дети в Орике - PullRequest
0 голосов
/ 28 января 2020

Учитывая следующие классы (геттеры, сеттеры и абстрактные реализации опущены для краткости) ...

public class Matcher {
   private String name;
   private Operation match;
}

public class MatcherDTO {
   private String name;
   private Operation matchExpression;
}

public abstract class Operation {

   // Parameters to the operation, which may be
   // Operation implementations or boxed 
   // primitives
   private List<Object> inputs;

   public abstract Class getResultType();
   ...
}

public class AttributeReference extends Operation {
   ...
}

public class Equals extends Operation {
   ...
}

...

... Я собрал следующую MapperFactory:

@Bean
public MapperFactory mapperFactory() {
    final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    mapperFactory.classMap(Matcher.class, MatcherDTO.class)
            .field("match", "matchExpression")
            .byDefault()
            .register();
    mapperFactory.classMap(Operation.class, Operation.class)
            .exclude("resultType")
            .byDefault()
            .register();
    return mapperFactory;
}

Когда я отправляю следующий запрос ...

{
    "name": "example-matcher",
    "matchOperation": { "eq": [ { "ref": [ "jdbc-resource", "login" ] }, { "ref": [ "ad-resource", "userPrincipalName" ] } ] }
}

... моя десериализация из JSON в MatcherDTO выглядит так, как я ожидаю:

this = {MatcherDTO}
+- name = {String} "example-matcher"
+- matchExpression = {Equals}
   +- inputs = {ArrayList}
      +- 0 = {AttributeReference}
         +- inputs = {ArrayList}
            +- 0 = {String} "jdbc-resource"
            +- 1 = {String} "login"
      +- 1 = {AttributeReference}
         +- inputs = {ArrayList}
            +- 0 = {String} "ad-resource"
            +- 1 = {String} "userPrincipalName"

Однако, когда я передаю свой MatcherDTO через маппер для создания Matcher, первый уровень рекурсии Операции корректен (а именно, экземпляр Equals), но вторые уровни неверны (а именно, экземпляры Object вместо экземпляров AttributeReference):

this = {Matcher}
+- name = {String} "example-matcher"
+- match = {Equals}
   +- inputs = {ArrayList}
      +- 0 = {Object}
      +- 1 = {Object}

Теперь я могу обойти это путем явного отображения подклассов Operation, например, так:

@Bean
public MapperFactory mapperFactory() {
    final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    mapperFactory.classMap(Matcher.class, MatcherDTO.class)
            .field("match", "matchExpression")
            .byDefault()
            .register();
    mapperFactory.classMap(Operation.class, Operation.class)
            .exclude("resultType")
            .byDefault()
            .register();
    mapperFactory.classMap(AttributeReference.class, AttributeReference.class)
            .exclude("resultType")
            .byDefault()
            .register();
    mapperFactory.classMap(Equals.class, Equals.class)
            .exclude("resultType")
            .byDefault()
            .register();
    // and so on...
    return mapperFactory;
}

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

...