lombok-mapstruct-binding
for this to work
Hi all, I am a bit new with mapstruct. Got the following situation:
Source:
public class OutcodeStats {
double avgPrice;
double avgPricePsf;
double avgRent;
double avgYield;
double growth1y;
double growth3y;
double growth5y;
String outcode;
int salesPerMonth;
int turnover;
long effectiveDate;
}
target:
public class GrowthStats {
public String status;
public String postcode;
public String postcode_type;
public String url;
public List<List<Object>> data;
public String process_time;
}
Basically, I only need to map growth1y, growth3y and growth5y to the list data
in target. How do I achieve that?
I have a situation where two target fields are depenging on the same source field, something like this
@Mapping(target = "data.targetA", source = "sourceA")
@Mapping(target = "data.targetB", source = "sourceA")
TargetModel jiraResponseToFmModel(SourceModel sourceModel);
targetA and targetB are both objects that hold different data based on sourceA. They also hold a UUID, which I need to generate, i.e. it does not come with sourceA. And the UUID needs to be the same for both targetA and targetB.
Normally I would look to use a custom function, but I'm not sure how I can preserve/reuse the UUID I generate from targetA to targetB or vice versa. Was thinking about @AfterMapping perhaps, would that work? Are there other options?
Hi, IDEA often shows a compiler error in one particular Mapping file - which always disappears as soon as I open the file in the editor. Does that sound familiar to anyone?
The error message / stacktrace I see in the problem view is as follows:
/Users/marcel/Git/recommenders/universal/platform/modules/universal-commons-worker/src/main/java/com/jetbrains/recommenders/universal/commons/workers/ArtifactsMapper.java
Error:(16, 8) java: Internal error in the mapping processor: java.lang.RuntimeException: javax.annotation.processing.FilerException: Attempt to recreate a file for type com.jetbrains.recommenders.universal.commons.workers.ArtifactsMapperGenerated at org.mapstruct.ap.internal.processor.MapperRenderingProcessor.createSourceFile(MapperRenderingProcessor.java:59) at org.mapstruct.ap.internal.processor.MapperRenderingProcessor.writeToSourceFile(MapperRenderingProcessor.java:39) at org.mapstruct.ap.internal.processor.MapperRenderingProcessor.process(MapperRenderingProcessor.java:29) at org.mapstruct.ap.internal.processor.MapperRenderingProcessor.process(MapperRenderingProcessor.java:24) at org.mapstruct.ap.MappingProcessor.process(MappingProcessor.java:283) at org.mapstruct.ap.MappingProcessor.processMapperTypeElement(MappingProcessor.java:263) at ...Caused by: javax.annotation.processing.FilerException: Attempt to recreate a file for type com.jetbrains.recommenders.universal.commons.workers.ArtifactsMapperGenerated at jdk.compiler/com.sun.tools.javac.processing.JavacFiler.checkNameAndExistence(JavacFiler.java:725) at jdk.compiler/com.sun.tools.javac.processing.JavacFiler.createSourceOrClassFile(JavacFiler.java:490) at jdk.compiler/com.sun.tools.javac.processing.JavacFiler.createSourceFile(JavacFiler.java:427) at org.mapstruct.ap.internal.processor.MapperRenderingProcessor.createSourceFile(MapperRenderingProcessor.java:56)
any solution for this ? facing similar issue
@Named("myMethod")
mapping method in @Named("myMapper")
Mapper C that I want to use in both mappers A and B. I've annotated them with @Mapper(uses = { C.class })
and the corresponding mapping methods in A and B are annotated with @Mapping(target = "...", source = "..." qualifiedByName = {"myMapper", "myMethod" })
. From the docs' sections 5.5 and 5.9 (link) I think this is how it should work (although I might be missing something, because I don't fully understand some parts, e.g. 5.6 - like, where is BaseEntity
coming from?). Anyway, my AImpl gets C injected (I use the constructor injection strategy, so can see that) - but it's not used. What am I doing wrong; how do I use a named method in C from A and B?
Hi,
according to the reference guide mapstruct always use the most specific mapper but it seems like this does not work properly if I use generics. Example:
@Mapper
public abstract class TestMapper {
public abstract void map(Source source, @MappingTarget Target target);
public <T> T mapField(Field<T> s) {
return s.getA();
}
public String mapSpecialField(SpecialField s) {
return s.getB();
}
@Data
public static class Source {
private Field<String> field1;
private SpecialField field2;
}
@Data
public static class Field<T> {
private T a;
}
@Data
public static class SpecialField extends Field<String> {
private String b;
}
@Data
public static class Target {
private String field1;
private String field2;
}
}
Result:
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-03-05T10:26:53+0100",
comments = "version: 1.4.2.Final, compiler: Eclipse JDT (IDE) 1.3.1100.v20200828-0941, environment: Java 14 (AdoptOpenJDK)"
)
@ApplicationScoped
public class TestMapperImpl extends TestMapper {
@Override
public void map(Source source, Target target) {
if ( source == null ) {
return;
}
target.setField1( mapField( source.getField1() ) );
target.setField2( mapField( source.getField2() ) );
}
}
Expected result:field2
gets mapped using mapSpecialField
.
I can add @Named
to select the right method but I thought that it should pick up that mapper automatically. Is there something I have missed or is this a bug?
Is there a reason why MapStruct won't convert a UUID
to a String
by default? IE, if I have FooEntity.pledgeId
which is a UUID
and FooAPI.pledgeId
which is a String
I have to add:
default String mapUUIDToString(UUID source) {
return source.toString();
}
to my mapper interface. Seems like this would be a fairly common mapping.
@Mapper( unmappedSourcePolicy = ReportingPolicy.ERROR,
unmappedTargetPolicy = ReportingPolicy.ERROR)
static public interface PojoMapper {
PojoMapper INSTANCE = Mappers.getMapper( PojoMapper.class );
@Mappings({
@Mapping(source = "x", target = "z"),
@Mapping(source = "a", target = "t"),
@Mapping(source = "listaItem", target = "listaItem2"),
@Mapping(source = "b", ignore = true) // ------------>>> this does not work. :(
})
Pojo2 pojo1ToPojo2(Pojo pojo1);
}
Hello,
I'm learning/training MapStruct
Please, why @AfterMapping code is not working?
Project repository: https://gitlab.com/cviniciusm/mapstructdemo2.git
MyEntity.java:
package br.eti.cvm.mapstructdemo2;
import lombok.*;
import java.time.LocalDate;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MyEntity {
private String name;
private LocalDate birthday;
}
MyDto.java:
package br.eti.cvm.mapstructdemo2;
import lombok.*;
import java.util.Date;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MyDto {
private String name;
private Date birthday;
private Integer age;
}
MyMapper.java:
package br.eti.cvm.mapstructdemo2;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import java.time.LocalDate;
import java.time.Period;
@Mapper
public interface MyMapper {
MyEntity toEntity(MyDto dto);
MyDto toDto(MyEntity entity);
@AfterMapping
default void updateAge(MyEntity entity, @MappingTarget MyDto dto) {
Period age = Period.between(entity.getBirthday(), LocalDate.now());
dto.setAge(age.getYears());
}
}
The generated MyMapperImpl.java:
package br.eti.cvm.mapstructdemo2;
import br.eti.cvm.mapstructdemo2.MyDto.MyDtoBuilder;
import br.eti.cvm.mapstructdemo2.MyEntity.MyEntityBuilder;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-03-10T18:35:44-0300",
comments = "version: 1.4.2.Final, compiler: javac, environment: Java 1.8.0_275 (AdoptOpenJDK)"
)
@Component
public class MyMapperImpl implements MyMapper {
@Override
public MyEntity toEntity(MyDto dto) {
if ( dto == null ) {
return null;
}
MyEntityBuilder myEntity = MyEntity.builder();
myEntity.name( dto.getName() );
if ( dto.getBirthday() != null ) {
myEntity.birthday( LocalDateTime.ofInstant( dto.getBirthday().toInstant(), ZoneOffset.UTC ).toLocalDate() );
}
return myEntity.build();
}
@Override
public MyDto toDto(MyEntity entity) {
if ( entity == null ) {
return null;
}
MyDtoBuilder myDto = MyDto.builder();
myDto.name( entity.getName() );
if ( entity.getBirthday() != null ) {
myDto.birthday( Date.from( entity.getBirthday().atStartOfDay( ZoneOffset.UTC ).toInstant() ) );
}
return myDto.build();
}
}
Hi @filiphr ,
I have a Quarkus
app that uses Mapstruct
.
Recently, I've updated Quarkus
to version 1.12.2.Final
and Mapstruct
to 1.4.2.Final
.
Since then, I started facing, sometimes, problems when packaging the app using Maven
.
Questions:
Do we need to create something like
@MapperConfig(componentModel = "cdi")
interface QuarkusMappingConfig {}
And then, use it like
@Mapper(config = QuarkusMappingConfig.class)
public interface BookMapper {
...
}
OR
@Mapper(componentModel = "cdi")
public interface BookMapper {
...
}
Thanks!
Hi there!
So now I am dealing with a legacy codebase which uses mapstruct 1.2.0.Final and I am trying to upgrade it to 1.4.2.Final. So with the 1.4.2.Final I am having the problem that generated mapper for AddressDto extends wrong class.
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
public class CountryDto {
String name;
String code;
@Mapper
@Component
public interface Converter {
CountryDto convert(Country from);
default List<CountryDto> convert(List<Country> from) {
if (from == null) {
return null;
}
return from.map(this::convert);
}
default List<Country> reverseConvert(List<CountryDto> from) {
if (from == null) {
return null;
}
return from.map(this::reverseConvert);
}
default Country reverseConvert(CountryDto from) {
if (from == null) {
return null;
}
return Country.builder()
.name(from.getName())
.code(from.getCode())
.build();
}
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
public class AddressDto {
CountryDto country;
String zipCode;
String city;
String street;
String houseNumber;
String apartmentNumber;
@Component
@Mapper(uses = CountryDto.Converter.class)
public static abstract class Converter {
@Autowired
private CountryDto.Converter countryMapper;
public Address reverseConvert(AddressDto from) {
if (from == null) {
return null;
}
return Address.builder()
.country(null)
.zipCode(from.zipCode)
.city(from.city)
.street(from.street)
.houseNumber(from.houseNumber)
.apartmentNumber(from.apartmentNumber)
.country(countryMapper.reverseConvert(from.country))
.build();
}
public abstract AddressDto convert(Address address);
}
}
So here is the generated mapper for AddressDto and as you can see it extends CountryDto.Converter but it should extend AddressDto.Converter. So what do think will be the best approach to resolve this issue? Maybe there can be done some configuration for mapstruct?
import vlo.domain.rest.controller.products.common.dto.CountryDto.Converter;
@Generated(
value = "org.mapstruct.ap.MappingProcessor"
)
@Component
public class AddressDto$ConverterImpl extends Converter {
@Autowired
private Converter converter;
@Override
public AddressDto convert(Address address) {
if ( address == null ) {
return null;
}
AddressDto addressDto = new AddressDto();
addressDto.setCountry( converter.convert( address.getCountry() ) );
addressDto.setZipCode( address.getZipCode() );
addressDto.setCity( address.getCity() );
addressDto.setStreet( address.getStreet() );
addressDto.setHouseNumber( address.getHouseNumber() );
addressDto.setApartmentNumber( address.getApartmentNumber() );
return addressDto;
}
}
@Mapper()
public interface StuffMapper {
StuffMapper INSTANCE = Mappers.getMapper(StuffMapper.class);
@Mapping(source = "foo", target = "foo2")
@Mapping(source = "bar", target = "baz")
@Mapping(target = "prop", expression = "java(null)")
Stuff thingToStuff(Thing thing);
@Mapping(source = "thing.foo", target = "foo2")
@Mapping(source = "thing.bar", target = "baz")
@Mapping(source = "prop", target = "prop")
Stuff thingToStuff(Thing thing, String prop);
}
thingToStuff(Thing thing)
to call thingToStuff(thing, null)
?