Двустороннее картирование бульдозера (String, String) с пользовательским преобразователем невозможно? - PullRequest
4 голосов
/ 18 февраля 2011

У меня есть отображение Dozer с пользовательским конвертером:

<mapping>
    <class-a>com.xyz.Customer</class-a>
    <class-b>com.xyz.CustomerDAO</class-b>
    <field custom-converter="com.xyz.DozerEmptyString2NullConverter">
        <a>customerName</a>
        <b>customerName</b>
    </field>
</mapping>

И конвертер:

public class DozerEmptyString2NullConverter extends DozerConverter<String, String> {

    public DozerEmptyString2NullConverter() {
        super(String.class, String.class);
    }

    public String convertFrom(String source, String destination) {
        String ret = null;
        if (source != null) {
            if (!source.equals(""))
            {
                ret = StringFormatter.wildcard(source);
            } 
        }
        return ret;
    }

    public String convertTo(String source, String destination) {
        return source;
    }
}

Когда я вызываю маппер в одном направлении (Customer -> CustomerDAO), вызывается метод convertTo.

Поскольку Dozer может обрабатывать двунаправленное отображение, я ожидаю, что, как только я вызову преобразователь в противоположном направлении, будет вызван метод convertFrom.

Но метод convertTo никогда не вызывается.

Я подозреваю, что проблема в том, что оба типа являются строками - но как я могу заставить эту работу работать?

В качестве обходного пути я создал два односторонних сопоставления: это стандартное решение или это ошибка?

Ответы [ 3 ]

2 голосов
/ 18 февраля 2011

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

  public Object convert(Object existingDestinationFieldValue, Object sourceFieldValue, Class<?> destinationClass, Class<?> sourceClass) {
    Class<?> wrappedDestinationClass = ClassUtils.primitiveToWrapper(destinationClass);
    Class<?> wrappedSourceClass = ClassUtils.primitiveToWrapper(sourceClass);

    if (prototypeA.equals(wrappedDestinationClass)) {
      return convertFrom((B) sourceFieldValue, (A) existingDestinationFieldValue);
    } else if (prototypeB.equals(wrappedDestinationClass)) {
      return convertTo((A) sourceFieldValue, (B) existingDestinationFieldValue);
    } else if (prototypeA.equals(wrappedSourceClass)) {
      return convertTo((A) sourceFieldValue, (B) existingDestinationFieldValue);
    } else if (prototypeB.equals(wrappedSourceClass)) {
      return convertFrom((B) sourceFieldValue, (A) existingDestinationFieldValue);
    } else if (prototypeA.isAssignableFrom(wrappedDestinationClass)) {
      return convertFrom((B) sourceFieldValue, (A) existingDestinationFieldValue);
    } else if (prototypeB.isAssignableFrom(wrappedDestinationClass)) {
      return convertTo((A) sourceFieldValue, (B) existingDestinationFieldValue);
    } else if (prototypeA.isAssignableFrom(wrappedSourceClass)) {
      return convertTo((A) sourceFieldValue, (B) existingDestinationFieldValue);
    } else if (prototypeB.isAssignableFrom(wrappedSourceClass)) {
      return convertFrom((B) sourceFieldValue, (A) existingDestinationFieldValue);
    } else {
      throw new MappingException("Destination Type (" + wrappedDestinationClass.getName()
          + ") is not accepted by this Custom Converter (" 
          + this.getClass().getName() + ")!");
    }

  }

Вместо использования методов convertFrom и convertTo (которые являются частью нового API), сделайте это оригинальным способом, вам необходимо реализовать CustomConverter.convert, как показано в учебнике .

0 голосов
/ 28 марта 2016

Я столкнулся с такой же проблемой через пару лет, и каким-то образом API-интерфейс DozerConverter, который является новым API, все еще не работает как двунаправленный !!

Итак, вместо того, чтобы вдаваться во все эти сложные решения, о которых говорилось здесь, я также создал 2 односторонних сопоставления, чтобы обойти эту проблему (с).И тогда мои конверсии начали работать.Я использую API DozerConverter, как показано ниже:

открытый класс MapToStringConverter расширяет DozerConverter

0 голосов
/ 09 июня 2014

У меня была такая же проблема, и в настоящее время (в Dozer 5.5.x) простого способа нет, но есть и более сложный.

Обратите внимание, что в JVM не включен диспетчер безопасности, илииначе вам нужно будет добавить несколько разрешений в правила безопасности.Это связано с тем, что это решение использует отражение для доступа к закрытым полям классов Dozer.

Вам необходимо расширить 2 класса: DozerBeanMapper и MappingProcessor.Вам также понадобится enum для указания и интерфейса, чтобы получить направление из вышеперечисленных классов.

Перечисление:

public enum Direction {
    TO,
    FROM;
}

Интерфейс:

public interface DirectionAware {
    Direction getDirection();
}

Расширяемый класс DozerBeanMapper:

public class DirectionAwareDozerBeanMapper extends DozerBeanMapper implements DirectionAware {
    private Direction direction;

    public DirectionAwareDozerBeanMapper(Direction direction) {
        super();
        this.direction = direction;
    }

    public DirectionAwareDozerBeanMapper(Direction direction, List<String> mappingFiles) {
        super(mappingFiles);
        this.direction = direction;
    }

    @Override
    protected Mapper getMappingProcessor() {
        try {
            Method m = DozerBeanMapper.class.getDeclaredMethod("initMappings");
            m.setAccessible(true);
            m.invoke(this);
        } catch (NoSuchMethodException|SecurityException|IllegalAccessException|IllegalArgumentException|InvocationTargetException e) {
            // Handle the exception as you want
        }

        ClassMappings arg1 = (ClassMappings)getField("customMappings");
        Configuration arg2 = (Configuration)getFieldValue("globalConfiguration");
        CacheManager arg3 = (CacheManager)getField("cacheManager");
        StatisticsManager arg4 = (StatisticsManager)getField("statsMgr");
        List<CustomConverter> arg5 = (List<CustomConverter>)getField("customConverters");
        DozerEventManager arg6 = (DozerEventManager)getField("eventManager");
        Map<String, CustomConverter> arg7 = (Map<String, CustomConverter>)getField("customConvertersWithId");

        Mapper mapper = new DirectionAwareMappingProcessor(arg1, arg2, arg3, arg4, arg5,
                                arg6, getCustomFieldMapper(), arg7, direction);

        return mapper;
    }

    private Object getField(String fieldName) {
        try {
            Field field = DozerBeanMapper.class.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(this);
        } catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) {
            // Handle the exception as you want
        }
        return null;
    }

    public Direction getDirection() {
        return direction;
    }
}

Расширяемый класс MappingProcessor:

public class DirectionAwareMappingProcessor extends MappingProcessor implements DirectionAware {
    private Direction direction;

    protected DirectionAwareMappingProcessor(ClassMappings arg1, Configuration arg2, CacheManager arg3, StatisticsManager arg4, List<CustomConverter> arg5, DozerEventManager arg6, CustomFieldMapper arg7, Map<String, CustomConverter> arg8, Direction direction) {
        super(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
        this.direction = direction;
    }

    public Direction getDirection() {
        return direction;
    }
}

Теперь используется.

1) Каждый раз, когда вы хотите отобразить один и тот же тип примитива(например, String-String), используйте DozerConverter с этим типом для обоих аргументов в качестве пользовательского конвертера в файле сопоставлений бульдозера.Реализация такого конвертера должна расширять: DozerConverter<String,String> и реализовывать MapperAware интерфейс.Важно, чтобы у вас было MapperAware в наличии, поскольку, имея маппер, вы сможете привести его к DirectionAware и затем получить направление.

Например:

public class MyMapper extends DozerConverter<String, String> implements MapperAware {
    private DirectionAware dirAware;

    public MyMapper(Class<String> cls) {
        super(cls, cls);
    }

    @Override
    public Object convert(Object existingDestinationFieldValue, Object sourceFieldValue, Class<String> destinationClass, Class<String> sourceClass) {
        if (dirAware.getDirection() == Direction.FROM) {
            // TODO convert sourceFieldValue for "FROM" direction and return it
        } else {
            // TODO convert sourceFieldValue for "TO" direction and return it
        }
    }

    @Override
    public void setMapper(Mapper mapper) {
        dirAware = (DirectionAware)mapper;
    }
}

2) Вам нужно создать 2 глобальных объекта Dozer mapper, по одному на каждое направление отображения.Они должны быть настроены с одинаковыми файлами сопоставления, но с другим аргументом направления.Например:

DirectionAwareDozerBeanMapper mapperFrom = DirectionAwareDozerBeanMapper(mappingFiles, Direction.FROM);
DirectionAwareDozerBeanMapper mapperTo = DirectionAwareDozerBeanMapper(mappingFiles, Direction.TO);

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

...