пользовательское отображение бульдозера

Я пытаюсь использовать Dozer для преобразования экземпляра

class Source {
  private List<Foo> foos = new ArrayList<Foo>();

  public List<Foo> getFoos() {
    return foos;
  }

  public void setFoos(List<Foo> foos) {
    this.foos = foos;
  }
}

к экземпляру:

class Target {
  private List<Foo> foos = new ArrayList<Foo>();

  public List<Foo> getFoos() {
    return foos;
  }
}

В коде Java я бы выполнил преобразование следующим образом

Source s = new Source();
Target t = new Target();
t.getFoos().addAll(s.getFoos());

Dozer не выполняет это преобразование по умолчанию, потому что Target не имеет свойства foos (только геттер).

На самом деле у меня много таких свойств, которые мне нужно отобразить. Один из вариантов — попросить Dozer отобразить частные поля напрямую, но это не совсем удовлетворительно. так как:

  • Мне нужно будет указать каждое поле, которое будет отображаться таким образом, по имени в конфигурации XML Dozer.
  • доступ к закрытым полям плохой

Есть ли способ лучше?


person Dónal    schedule 21.04.2011    source источник
comment
Вы знаете, что t.getFoos().addAll(s.getFoos()); приведет к NPE для приведенной выше реализации Target, не так ли?   -  person Thomas    schedule 21.04.2011
comment
извините, я сделал ошибку в примере моего кода. Я исправил это сейчас   -  person Dónal    schedule 21.04.2011


Ответы (4)


Нет простого способа обойти это, кроме флага is-accessible. Но вы можете определить собственный преобразователь, который использует геттер для выполнения:

t.getFoos().addAll(s.getFoos());

Это было бы очень тяжело и много работы. Вам потребуется определить собственный преобразователь (см. http://dozer.sourceforge.net/documentation/customconverter.html) между Source и Target, которые использовали геттер вместо сеттера:

public class TestCustomConverter implements CustomConverter {

  public Object convert(Object destination, Object source, Class destClass, Class sourceClass) {
    if (source == null) {
      return null;
    }
    if (source instanceof Source) {
      Target dest = null;
      // check to see if the object already exists
      if (destination == null) {
        dest = new Target();
      } else {
        dest = (Target) destination;
      }
      dest.getFoos().addAll(((Source)source).getFoos());
      return dest;
    } else if (source instanceof Target) {
      Source dest = null;
      // check to see if the object already exists
      if (destination == null) {
        dest = new Source();
      } else {
        dest = (Source) destination;
      }
      dest.setFoos(((Target)source).getFoos());
      return dest;
    } else {
      throw new MappingException("Converter TestCustomConverter used incorrectly. Arguments passed in were:"
          + destination + " and " + source);
    }
  } 

Думаю, удачи

person mark-cs    schedule 21.04.2011
comment
Теперь, когда я исправил NPE, не могли бы вы показать, как я могу использовать специальный конвертер для решения этой проблемы? - person Dónal; 21.04.2011
comment
Спасибо за вашу помощь, но приведенное выше решение будет работать только для сопоставления Source.foos с Target.foos. Мне нужно решение, которое будет работать для сопоставления любых двух свойств типа List, где в целевом классе есть только геттер. - person Dónal; 22.04.2011
comment
@don Это правда, но вы не можете отображать списки в общем, так как им нужно знать родительский класс для использования геттера. Это единственное решение, которое я могу придумать. - person mark-cs; 22.04.2011

Вы можете добавить метод в Target:

public void addFoo(Foo item) {
    foos.add(item);
}

<mapping>
  <class-a>Source</class-a>
  <class-b>Target</class-b>
  <field>
    <a>foos</a>
    <b set-method="addFoo" type="iterate">foos</b>
  </field>
</mapping>

Возможно, предложите функцию, позволяющую использовать EL в выражении сеттера или геттера.

person Corneil du Plessis    schedule 29.05.2011

Вы можете проверить ModelMapper в качестве альтернативы Dozer. Обработка этого сценария тривиальна:

ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
  .enableFieldMatching(true)
  .setFieldAccessLevel(AccessLevel.PRIVATE);

Это включило сопоставление полей для всех закрытых полей. Чтобы выполнить сопоставление:

Target target = modelMapper.map(source, Target.class);

Посетите сайт ModelMapper для получения дополнительной информации:

http://modelmapper.org

person Jonathan    schedule 24.06.2011

Если, например, у вас нет установщика для значения списка (как у меня по какой-то причине...), вы можете использовать сопоставление полей в сочетании с «этим», чтобы идентифицировать атрибут, который вы можете использовать «ключ»:

<field custom-converter="de.xyz.custom.MyConverter">
    <a key="variablename">this</a>
    <b>targetvariablename</b>
</field>

Затем вы можете приступить к реализации преобразователя. Вам будет предоставлен объект, содержащий поле «имя переменной» в качестве источника. теперь вы можете манипулировать исходным объектом так, как вам нужно (в этом случае используйте геттер, получите список, addAll() и все готово).

person fl0w    schedule 21.11.2017