受this answer的启发,我写了一个自定义转换器(你可以在Github repo中找到整个工作示例)。为Dozer转换:
public class MyEntity {
private List<ObjectId> attachmentIds;
public List<ObjectId> getAttachmentIds() { return attachmentIds; }
public void setAttachmentIds(List<ObjectId> attachmentIds) {
this.attachmentIds = attachmentIds;
}
}
它的DTO:
public class MyEntityDto {
private List<FileDataDto> attachments;
public List<FileDataDto> getAttachments() { return attachments; }
public void setAttachments(List<FileDataDto> attachments) {
this.attachments = attachments;
}
}
MyEntity
仅保存存储在Mongo数据库中的文件的ID。它的DTO被发送到JSON的前端,应该包含文件的id和文件名(这是FileDataDto
类的内容)。我的转换器:
public class FileIdToFileDataConverter extends DozerConverter<ObjectId, FileDataDto> {
public FileIdToFileDataConverter() {super(ObjectId.class, FileDataDto.class); }
@Override
public FileDataDto convertTo(ObjectId source, FileDataDto destination) {
if (source == null) {
return null;
}
FileDataDto fileData = destination == null ? new FileDataDto() : destination;
fileData.setId(source.toString());
// fetch the file from repository and update the name from db
fileData.setFilename("myfile.txt");
return fileData;
}
@Override
public ObjectId convertFrom(FileDataDto source, ObjectId destination) {
return source == null ? null : new ObjectId(source.getId());
}
}
转换在MyEntity
- &gt;中按预期工作{{1个方向。然而,它恰恰相反。它使用Dozer创建的MyEntityDto
(作为ObjectId
参数传递)而不是转换器返回的destination
。这个测试
@Test
public void dtoToMyEntity() {
MyEntityDto dto = new MyEntityDto();
FileDataDto fileData = new FileDataDto();
fileData.setFilename("file.txt");
fileData.setId(new ObjectId().toString());
dto.setAttachments(Arrays.asList(fileData));
MyEntity myEntity = mapper.map(dto, MyEntity.class);
assertEquals(fileData.getId(), myEntity.getAttachmentIds().get(0).toString());
}
失败并显示示例消息:
org.junit.ComparisonFailure:
Expected :56b0a9d110a937fc32a6db18
Actual :56b0a9d110a937fc32a6db19
您可以在Github repo中找到我使用的整个测试和配置。
如何使转换器以两种方式工作?
答案 0 :(得分:1)
它与dozer中的bug有关,导致在通过API映射时不使用自定义转换器:https://github.com/DozerMapper/dozer/issues/242
因此您可以通过xml提供映射:
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
http://dozer.sourceforge.net/schema/beanmapping.xsd">
<configuration>
<custom-converters>
<converter type="com.example.mapping.FileIdToFileDataConverter">
<class-a>org.bson.types.ObjectId</class-a>
<class-b>com.example.mapping.entity.FileDataDto</class-b>
</converter>
</custom-converters>
</configuration>
<mapping>
<class-a>com.example.mapping.entity.MyEntity</class-a>
<class-b>com.example.mapping.entity.MyEntityDto</class-b>
<field>
<a>attachmentIds</a>
<b>attachments</b>
<a-hint>org.bson.types.ObjectId</a-hint>
<b-hint>com.example.mapping.entity.FileDataDto</b-hint>
</field>
</mapping>
</mappings>
然后
mapper.setMappingFiles(Arrays.asList("dozerconfig.xml"));
或者,如果您不想使用xml,可以创建使用自己的ObjectIdFactory 的变通方法:
mapping(type(ObjectId.class).beanFactory(ObjectIdFactory.class), FileDataDto.class)
.fields(this_(), this_(), customConverter(FileIdToFileDataConverter.class));
工厂类
public class ObjectIdFactory implements BeanFactory {
@Override
public Object createBean(Object source, Class<?> sourceClass, String targetBeanId) {
if (source == null) {
return null;
}
if (source instanceof ObjectId) {
return source; // we can return source, because it's immutable
}
if (source instanceof String) {
return new ObjectId((String) source);
}
if (source instanceof FileDataDto) {
return new ObjectId(((FileDataDto) source).getId());
}
throw new MappingException("ObjectId should be of type ObjectId, String or FileDataDto");
}
}
此workaroud工作的原因以及为什么ID不匹配:
Dozer默认使用类的no-args构造函数来实例化空值。 ObjectId是不可变类,它的no-args构造函数根据时间戳创建新实例。
答案 1 :(得分:0)
一个更简单的选择是使用MapStruct,它支持开箱即用的不可变对象(including Lombok's and Immutable's builders)。
最小代码示例(来自文档):
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDto carToCarDto(Car car);
}
// Usage:
CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);