I am using following mapper to map entities:
public interface AssigmentFileMapper {
AssigmentFileDTO assigmentFileToAssigmentFileDTO(AssigmentFile assigmentFile);
AssigmentFile assigmentFileDTOToAssigmentFile(AssigmentFileDTO assigmentFileDTO);
#Mapping(target = "data", ignore = true)
List<AssigmentFileDTO> assigmentFilesToAssigmentFileDTOs(List<AssigmentFile> assigmentFiles);
List<AssigmentFile> assigmentFileDTOsToAssigmentFiles(List<AssigmentFileDTO> assigmentFileDTOs);
}
I need to ignore the "data" field only for entities that mapped as collection.
But it looks like #Mapping works only for single entities. Also I've noticed that generated method assigmentFilesToAssigmentFileDTOs just uses assigmentFileToAssigmentFileDTO in for-loop. Is there any solution for that?
MapStruct uses the assignment that it can find for the collection mapping. In order to achieve what you want you will have to define a custom method where you are going to ignore the data field explicitly and then use #IterableMapping(qualifiedBy) or #IterableMapping(qualifiedByName) to select the required method.
Your mapper should look like:
public interface AssigmentFileMapper {
AssigmentFileDTO assigmentFileToAssigmentFileDTO(AssigmentFile assigmentFile);
AssigmentFile assigmentFileDTOToAssigmentFile(AssigmentFileDTO assigmentFileDTO);
#IterableMapping(qualifiedByName="mapWithoutData")
List<AssigmentFileDTO> assigmentFilesToAssigmentFileDTOs(List<AssigmentFile> assigmentFiles);
List<AssigmentFile> assigmentFileDTOsToAssigmentFiles(List<AssigmentFileDTO> assigmentFileDTOs);
#Named("mapWithoutData")
#Mapping(target = "data", ignore = true)
AssignmentFileDto mapWithouData(AssignmentFile source)
}
You should use org.mapstruct.Named and not javax.inject.Named for this to work. You can also define your own annotation by using org.mapstruct.Qualifier
You can find more information here in the documentation.
Related
In my current project the names of the model class fields are German. The fields are all annotated with #JsonProperty for the English translation of the names. E.g. #JsonProperty(value = "operation"). Is there a way in the configuration that the mapping of the fields is done using the JsonProperty annotation?
Example:
public class Auftrag {
#JsonProperty(value = "orderType")
private String auftragsart;
...
}
public class OrderDto {
private String orderType;
}
MapStruct uses the Java Bean convention to detect the properties. This means that it looks in the getters and setters.
Out-of-the-box you cannot use the #JsonProperty. However, you can create your own AccessorNamingStrategy that will provide the properties based on #JsonProperty. The AccessorNamingStrategy gives you access to the Abstract syntax tree, which means you can look for fields in types, check their annotations and check their values.
Keep in mind that MapStruct will only ask to get the property for a method, so you would need to get the property name, then find the field in the type, then look for the #JsonProperty annotation and its value.
You can read more about the AccessorNamingStrategy here in the documentation.
I want to avoid the human mistake in mapping objects together so I use the map-struct package. but there are situations I should manually assign fields like renaming them. like this method
#Mapper(componentModel = "spring")
public interface ItemMapper extends EntityMapper<ItemDTO, Item> {
#Mapping(target = "itemTeplate", source = "template")
Item toEntity(Entity entity);
}
Is there any way to generate the class field names dynamically for this usage and get an error when changing the naming and have an autocomplete class like fields? like below picture
#Mapper(componentModel = "spring")
public interface ItemMapper extends EntityMapper<ItemDTO, Item> {
#Mapping(target = EntityFields.ITEM_TEMPLATE, source = ItemFields.TEMPLATE)
Item toEntity(Entity entity);
}
MapStruct is an annotation processor and code generator which internals work based on the reflection. The reflection-based look-up for the fields and getters/setters is based on the String matching.
I fully understand your worries, however, consider these facts:
Replacing such string literal with enumeration doesn't help you much and adds only an additional layer. As long as the object field is changed, the enumeration has to be changed as well. To be the devil's advocate, I admit you might want to use it anyway in case of a lot of similar mappings - however, for that I remind you that the mappings can be inherited.
On compilation, MapStruct correctly throws a warning when a field is unmapped, which might happen when a new field is added. If the field is not found at all, i.e. the field is either modified or removed, the compilation fails. MapStruct follows the fail-fast principle.
MapStruct is mapping all the properties of source and destination by default if they have same name. The ignore element in #Mapping can be used for omitting any field mapping. But that's not I want. I want control over the mapping strategy. I want to specify something like:
#Mapper(STRATEGY=MAPPING_STRATEGY.SPECIFIED)
public interface EmployeeToEmployeeDTOMapper {
#Mappings({
#Mapping(target="id", source="id"),
#Mapping(target="name", source="name")
})
public EmployeeDTO employeeToEmployeeDTO (Employee emp);
}
Now this mapping is only meant to map id and name from source to destination. No other fields should be mapped unless specified in the mappings annotation.
As of MapStruct 1.3, the #BeanMapping(ignoreByDefault = true) annotation can be added to the mapping method to achieve this result:
public interface EmployeeToEmployeeDTOMapper {
#BeanMapping(ignoreByDefault = true)
#Mapping(target="id", source="id")
#Mapping(target="name", source="name")
EmployeeDTO employeeToEmployeeDTO(Employee emp);
}
Per the Javadocs of the ignoreByDefault annotation element:
Default ignore all mappings. All mappings have to be defined manually. No automatic mapping will take place. No warning will be issued on missing target properties.
What you are looking for is a feature request in #1392. There is a pending PR so it would be available to use in the next version (1.3.0). The final API is not yet defined. Follow the issue and the PR to be notified when it gets done
I've a question in spring data elasticsearch.I would like to know how whether we can set the annotation values of #Document annotation from a properties file or set it dynamically.
For eg :-
#Document(indexName = "myindex",type="mytype")
public class DocumentModel {
......
}
Here,I want to set the values of this annotation from a .properties file or use some setter methods for the same instead of hard coding it. Is there any proper way to do this ? Please help!
If you're using elasticsearchTemplate, there is a simpler variant, you can do it like this:
IndexQuery indexQuery = new IndexQueryBuilder()
.withId(docModel.getId())
.withObject(docModel)
.withIndex("myindex"+docModel.getUserId()).withTypes(<type_name>).build();
the call to withIndex("...") will override whatever index name you have in the #Document annotation
I know how I can make Jackson to ignore any additional fields in Json, simply by adding
#JsonIgnoreProperties(ignoreUnknown = true):
#JsonIgnoreProperties(ignoreUnknown = true)
class MyDto {
int someField;
}
But side-effect of this is that Jackson now also accepts incomplete JSON and fills missing fields with nulls.
How can I enforce Jackson to require every field to exist in json and still ignore additional fields in it?
Thank you.
Jackson explicitly does NOT validate logical POJO contents; instead, you are recommended to use Bean Validation (JSR-303, see http://en.wikipedia.org/wiki/Bean_Validation) API implementation; for example one provided by Hibernate project: http://hibernate.org/validator/
This is the approach many frameworks take; for example, DropWizard supports data-binding using Jackson, and then validation (after data-bind, before business logic run) using Bean Validation.
In order to check if all properties needed are available you need to add the required anotation to the property.
#JsonProperty(value = "response", required = true)
public SomeResponse response;