为什么推土机将空源对象传递给我的可配置自定义转换器?

时间:2013-05-16 20:53:45

标签: java dozer

我正在使用Dozer版本5.4.0。

我有一个带有Map的类,另一个带有List的类。我正在尝试编写一个自定义转换器,它将获取地图的值并将其放入List中。但问题是,转换器总是传递给Map的null源对象,即使父源具有填充的Map。我无法弄清楚为什么会发生这种情况,我认为转换器应该传递一个填充的Map对象。

以下是一些编译并显示问题的源代码:

package com.sandbox;

import org.dozer.DozerBeanMapper;
import org.dozer.loader.api.BeanMappingBuilder;
import org.dozer.loader.api.FieldsMappingOptions;

public class Sandbox {

    public static void main(String[] args) {
        DozerBeanMapper mapper = new DozerBeanMapper();
        mapper.addMapping(new MappingConfig());

        ClassWithMap parentSource = new ClassWithMap();
        ClassWithList parentDestination = mapper.map(parentSource, ClassWithList.class);

        int sourceMapSize = parentSource.getMyField().size();
        assert sourceMapSize == 1;
        assert parentDestination.getMyField().size() == 1;    //this assertion fails!
    }

    private static class MappingConfig extends BeanMappingBuilder {

        @Override
        protected void configure() {
            mapping(ClassWithMap.class, ClassWithList.class)
                    .fields("myField", "myField",
                            FieldsMappingOptions.customConverter(MapToListConverter.class, "com.sandbox.MyMapValue"));
        }
    }


}

如您所见,第二个断言失败了。这是我正在使用的其他类。

MapToListConverter.java:

package com.sandbox;

import org.dozer.DozerConverter;
import org.dozer.Mapper;
import org.dozer.MapperAware;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class MapToListConverter extends DozerConverter<Map, List> implements MapperAware {

    private Mapper mapper;

    public MapToListConverter() {
        super(Map.class, List.class);
    }

    @Override
    public List convertTo(Map source, List destination) {   //source is always null, why?!
        List convertedList = new ArrayList();
        if (source != null) {
            for (Object object : source.values()) {
                Object mappedItem = mapper.map(object, getDestinationClass());
                convertedList.add(mappedItem);
            }
        }
        return convertedList;
    }

    private Class<?> getDestinationClass() {
        try {
            return Class.forName(getParameter());
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    public Map convertFrom(List source, Map destination) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setMapper(Mapper mapper) {
        this.mapper = mapper;
    }
}

ClassWithMap.java:

package com.sandbox;

import java.util.HashMap;
import java.util.Map;

public class ClassWithMap {

    private Map<String, MyMapValue> myField;

    public Map<String, MyMapValue> getMyField() {   //this method gets called by dozer, I've tested that with a break point
        if (myField == null) {
            myField = new HashMap<String, MyMapValue>();
            myField.put("1", new MyMapValue());
        }
        return myField; //myField has an entry in it when called by dozer
    }

    public void setMyField(Map<String, MyMapValue> myField) {
        this.myField = myField;
    }

}

ClassWithList.java:

package com.sandbox;

import java.util.List;

public class ClassWithList {

    private List<MyMapValue> myField;

    public List<MyMapValue> getMyField() {
        return myField;
    }

    public void setMyField(List<MyMapValue> myField) {
        this.myField = myField;
    }
}

MyMapValue.java

package com.sandbox;

public class MyMapValue {

}

问题似乎出现在推土机的MapFieldMap.getSrcFieldValue方法中。这些评论由我添加:

  @Override
  public Object getSrcFieldValue(Object srcObj) {
    DozerPropertyDescriptor propDescriptor;
    Object targetObject = srcObj;

    if (getSrcFieldName().equals(DozerConstants.SELF_KEYWORD)) {
      propDescriptor = super.getSrcPropertyDescriptor(srcObj.getClass());
    } else {
      Class<?> actualType = determineActualPropertyType(getSrcFieldName(), isSrcFieldIndexed(), getSrcFieldIndex(), srcObj, false);
      if ((getSrcFieldMapGetMethod() != null)
          || (this.getMapId() == null && MappingUtils.isSupportedMap(actualType) && getSrcHintContainer() == null)) {
        // Need to dig out actual map object by using getter on the field. Use actual map object to get the field value
        targetObject = super.getSrcFieldValue(srcObj);

        String setMethod = MappingUtils.isSupportedMap(actualType) ? "put" : getSrcFieldMapSetMethod();
        String getMethod = MappingUtils.isSupportedMap(actualType) ? "get" : getSrcFieldMapGetMethod();
        String key = getSrcFieldKey() != null ? getSrcFieldKey() : getDestFieldName();

        propDescriptor = new MapPropertyDescriptor(actualType, getSrcFieldName(), isSrcFieldIndexed(), getDestFieldIndex(),
                setMethod, getMethod, key, getSrcDeepIndexHintContainer(), getDestDeepIndexHintContainer());
      } else {
        propDescriptor = super.getSrcPropertyDescriptor(srcObj.getClass());
      }
    }

    Object result = null;
    if (targetObject != null) {
      result = propDescriptor.getPropertyValue(targetObject); //targetObject is my source map, but the result == null
    }

    return result;

  }

1 个答案:

答案 0 :(得分:2)

我想出了如何解决这个问题。仍然不确定它是否是一个错误,但我认为它是。解决方案是更改我的配置,说:

        mapping(ClassWithMap.class, ClassWithList.class, TypeMappingOptions.oneWay())
                .fields("myFields", "myFields"
                        , FieldsMappingOptions.customConverter(MapToListConverter.class, "com.sandbox.MyMapValue")
                );

修复程序位于TypeMappingOptions.oneWay()。当它是双向的时,推土机MappingsParser会尝试使用导致我问题的MapFieldMap

    // iterate through the fields and see wether or not they should be mapped
    // one way class mappings we do not need to add any fields
    if (!MappingDirection.ONE_WAY.equals(classMap.getType())) {
      for (FieldMap fieldMap : fms.toArray(new FieldMap[]{})) {
        fieldMap.validate();

        // If we are dealing with a Map data type, transform the field map into a MapFieldMap type
        // only apply transformation if it is map to non-map mapping.
        if (!(fieldMap instanceof ExcludeFieldMap)) {
          if ((isSupportedMap(classMap.getDestClassToMap())
                  && !isSupportedMap(classMap.getSrcClassToMap()))
              || (isSupportedMap(classMap.getSrcClassToMap())
                  && !isSupportedMap(classMap.getDestClassToMap()))
              || (isSupportedMap(fieldMap.getDestFieldType(classMap.getDestClassToMap()))
                  && !isSupportedMap(fieldMap.getSrcFieldType(classMap.getSrcClassToMap())))
              || (isSupportedMap(fieldMap.getSrcFieldType(classMap.getSrcClassToMap())))
                  && !isSupportedMap(fieldMap.getDestFieldType(classMap.getDestClassToMap()))) {
            FieldMap fm = new MapFieldMap(fieldMap);
            classMap.removeFieldMapping(fieldMap);
            classMap.addFieldMapping(fm);
            fieldMap = fm;
          }
        }