我正在使用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;
}
答案 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;
}
}