New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for optional subtype-only source properties #2438
Comments
Thanks for the detailed explanation @mjustin. I guess you are looking for MapStruct to generate your default Just so I understand you correctly. The |
…under test and fixed inheritence issues.
…le. fixed missing newline in mapper used for testing issue 2438.
mapstruct#131, mapstruct#2438, mapstruct#366: Added support for SubClassMapping annotations. This will allow for hierarchy mappings.
@filiphr Sorry I'm just now getting back to your question. Generating the @Mapping(target = "target1", source = "property1")
@Mapping(target = "target2", source = "property2")
@Mapping(target = "target3", source = "property3")
@Mapping(target = "target4", source = "subclassProperty", ifInstance = SourceSubclass.java)
@Mapping(target = "target5", expression = "java(null)")
Target map(Source source); |
I was playing around a bit more, and stumbled upon another approach using existing functionality to achieving this mapping: @Mapper
public interface SourceMapper {
@Mapping(target = "target1", source = "property1")
@Mapping(target = "target2", source = "property2")
@Mapping(target = "target3", source = "property3")
@Mapping(target = "target4", source = ".", qualifiedByName = "target4")
@Mapping(target = "target5", expression = "java(null)")
Target map(Source source);
@Named("target4")
String getTarget4(Source source) {
return source instanceof SourceSubclass ? (((SourceSubclass) source).getSubclassProperty()) : null;
}
} In some ways I think it's cleaner than what I had as the current workaround, though it does require some manual field mapping that in a more convenient world the framework might be able to take care of. |
Just letting you know that when the PR for 131, 2438 and 366 (see mentions above) is finished you will be able to generate the instanceof code from your first example by adding the @Mapper
public interface SourceMapper {
@SubClassMapping( sourceClass = SourceSubclass.class, targetClass = Target.class )
@Mapping( target = "target1", source = "property1" )
@Mapping( target = "target2", source = "property2" )
@Mapping( target = "target3", source = "property3" )
@Mapping( target = "target4", expression = "java(null)" )
@Mapping( target = "target5", expression = "java(null)" )
Target mapSuperclass(Source source);
@InheritConfiguration( name = "mapSuperclass" )
@Mapping( target = "target4", source = "subclassProperty" )
@Mapping( target = "target5" ) // Have to declare in order to override with default behavior
Target mapSubclass(SourceSubclass source);
} The Any suggestions that might improve the |
I like the approach by @Zegveld for this one. I would even go so far and say that the mapping should look like: @Mapper
public interface SourceMapper {
@SubClassMapping( sourceClass = SourceSubclass.class, targetClass = Target.class )
@Mapping( target = "target1", source = "property1" )
@Mapping( target = "target2", source = "property2" )
@Mapping( target = "target3", source = "property3" )
@Mapping( target = "target4", ignore = true )
@Mapping( target = "target5", ignore = true )
Target mapSuperclass(Source source);
@InheritConfiguration( name = "mapSuperclass" )
@Mapping( target = "target4", source = "subclassProperty" )
@Mapping( target = "target5" ) // Have to declare in order to override with default behavior
Target mapSubclass(SourceSubclass source);
} Nice work @Zegveld, I only need to spend some good time in going through the entire PR 😄 |
…2512) Add new `@SubclassMapping` for creating Downcast mapping. When a parent mapping method is annotated with `@SubclassMapping` it will now generate an instanceof check inside the parent mapping and generate the subclass mappings if they are not manually defined. There is also `SubclassExhaustiveStrategy` for controlling what MapStruct should do in case the target type is abstract and there is no suitable way to create it.
@filiphr I've confirmed that the following mapper works for my example in 1.5.0-SNAPSHOT: @Mapper
public interface SubclassMapper {
@SubclassMapping(source = SourceSubclass.class, target = Target.class)
@Mapping(target = "target1", source = "property1")
@Mapping(target = "target2", source = "property2")
@Mapping(target = "target3", source = "property3")
@Mapping(target = "target4", expression = "java(null)")
@Mapping(target = "target5", expression = "java(null)")
Target map(Source source);
@InheritConfiguration(name = "map")
@Mapping(target = "target4", source = "subclassProperty")
@Mapping(target = "target5") // Have to declare in order to override with default behavior
Target mapSubclass(SourceSubclass source);
} |
Thanks for testing this out @mjustin |
My source type is a superclass with a certain set of properties, with a subclass that adds additional properties. My target type contains properties to be mapped from the superclass, as well as some optional properties to be mapped if the source is an instance of the subclass. It would be convenient to be able to be able to express this mapping relationship in a single annotation-based mapping method, rather than using multiple mapping methods with a custom mapping method to tie them together.
This is certainly related to #131, and possibly involves the same solution. The difference is that that issue concerns mapping to different target types based on the source type, whereas this involves mapping different target types to a single source type, with optional fields for subclasses.
Example
Types
Mapper
This is an solution to the problem using the features currently available in MapStruct.
The text was updated successfully, but these errors were encountered: