Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Feature] Conditional mapping #2051

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

Closed
pherklotz opened this issue Mar 24, 2020 · 19 comments · Fixed by #2403
Closed

[Feature] Conditional mapping #2051

pherklotz opened this issue Mar 24, 2020 · 19 comments · Fixed by #2403
Assignees
Milestone

Comments

@pherklotz
Copy link

Feature proposal

Specify a condition (similar to the expression) for a target. If the condition evaluates to true the mapping will be done. Otherwise nothing happens or the default will be used if specified.

Motivation

Whether a null check nor a presence checker method are sometimes mighty enough to express whether a property should be mapped or not. We use MapStruct to map Protobuf v3 Messages. These can contain oneof fields which can contain only exact one or zero values. If one of the specified values is a primitive datatype like int or long there is no hasXYZ method generated and the default value will be returned from the getter (e.g. zero) this is also the case if another value type is the in the oneof! You have to check the case-Field of the protobuf message to find out which of the oneofs is set. See protobuf documentation about that:

Example

Protobuf:

...
oneof foo {
  uint32 bar = 1;
  uint32 baz = 2;
}
...

Generated mapper code for this:

o1.setBar( o.getBar() );
o1.setBaz( o.getBaz() );

But only one should be set. With the proposed conditional mapping I could write a condition to check the case-Field of the source to find out which property should be set.

Example for the usage of the proposed feature

Mapper interface

@Mapping(target="baz", condition="java( bar.getBaz()==42 )")
Foo mapFoo(Bar bar);

Generated mapping code for property "baz"

if(bar.getBaz()==42) {
  foo.setBaz( bar.getBaz() );
}

If you are open to adopt this feature I would provide a PR.

@filiphr
Copy link
Member

filiphr commented Jun 6, 2020

Thanks for the suggestion @pherklotz. We also just received the same request in #2107. Their Option 1 is identical to yours.

I like the proposal here, with one difference instead of using condition, I think that conditionExpression makes more sense.

If you are still interested in providing a PR can you please sync with @Desislav-Petrov?

Looking at this I got an idea that could be something interesting is as well. We can add a new annotation named @Condition which we can then use in condition. That way you can use some utils for it.

e.g.

@Condition("is42")
default boolean is42(int value) {
    return value == 42;
}

@Condition
default boolean isNotEmpty(String value) {
    return value != null && value.length() > 0;
}

@Mapping(target = "baz", condition = "is42")
@Mapping(target = "someString", condition = "isNotEmpty")
For mapFoo(Bar bar);

We can even look for a single @Condition method which has the source type as input and has a boolean as output, or allow specifying on the @Condition whether it applies to all or not.

This might even solve #2084

@Desislav-Petrov
Copy link

Hi @filiphr,

I think I like your proposal more because in addition to what you said, this way we will have proper java code versus "java as a string" as in condition.

Thanks,
Des

@filiphr
Copy link
Member

filiphr commented Jun 6, 2020

I don't mind having both options (conditionExpression and condition). I personally am always more for java code vs an expression (java as string).

@ivangsa
Copy link

ivangsa commented Jun 7, 2020

Yes, I think the @Condition proposal would be an even better solution for #2084 (when it can be applied to all properties of type)

@filiphr
Copy link
Member

filiphr commented Jun 7, 2020

For #2084 it can even work out of the box by defining some method like:

default <T> boolean isPresent(Optional<T> optional) {
    return optional.isPresent();
}

In theory if we add the support for condition we can have some out of the box conditions that we'll be automatically reused, similar to how it is done for the mapping methods

@Desislav-Petrov
Copy link

Thanks for the good ideas, I've just started working on a PR and would have an initial impl shortly.

@filiphr
Copy link
Member

filiphr commented Jun 13, 2020

That's great. Looking forward to reviewing it.

@pherklotz
Copy link
Author

Thanks for all your comments. I really like your idea @filiphr. This allows more complex checks with good readability. @Desislav-Petrov can I help you with the implementation?

@Desislav-Petrov
Copy link

@pherklotz why not divide and conq on this one - I'm working on introducing the condition = method name bit. Once i'm done with this you can finish off the second part about introducing the @condition ?

@filiphr
Copy link
Member

filiphr commented Jun 21, 2020

Thanks for the work on this @pherklotz and @Desislav-Petrov. Let us know if you need some help.

One a side note something like this might help us with the presence check for #1075. We'll need a containsKey check as a presence check there.

@Desislav-Petrov
Copy link

Desislav-Petrov commented Jul 11, 2020

Hi,

PR: #2148

Haven't implemented the @condition bit as I haven't heard if @pherklotz is interested to finish it off. I'm happy to do it as an extension if I don't hear back.

PR feedback would be appreciated.

Thanks,
Des

@kevinprotoss
Copy link

Hi,

Cool, if this feature could be done. We require this feature for our use case as well and I also like this solution.

Bg,

@kevinprotoss
Copy link

Hi,

the solution part from @Desislav-Petrov works already for me. How about the part for @condition annotation? Will it still be implemented.

If not, I'd like to have a try. @filiphr Which parts should I change, if I wanna implement this new @condition annotation?

Bg,
Kevin

@Desislav-Petrov
Copy link

I'm doing the @condition bit too, was waiting to hear if @pherklotz still wants to do part of the work or not. Since I haven't heard from him for a while I will continue with adding it.

@keltharan
Copy link

Hi,
is it possible to use multiple parameters for the given condition. I would like to use protobuf oneof and this condition annotation in combination with a Map<String, String> mapping. So that i can create a generic mapper referring to the required fields in the provided map.

BR

@filiphr filiphr added this to the 1.5.0 milestone Jan 11, 2021
filiphr added a commit to filiphr/mapstruct that referenced this issue Apr 4, 2021
@filiphr filiphr mentioned this issue Apr 4, 2021
1 task
filiphr added a commit to filiphr/mapstruct that referenced this issue Apr 4, 2021
@filiphr filiphr self-assigned this Apr 5, 2021
filiphr added a commit to filiphr/mapstruct that referenced this issue Apr 24, 2021
filiphr added a commit that referenced this issue Apr 25, 2021
@filiphr
Copy link
Member

filiphr commented Apr 25, 2021

This has now been implemented in the same style as the other functionality of MapStruct.

The new @Condition annotation can be used to mark that a method is a condition check method. There are 2 new options in the @Mapping: conditionQualifiedBy and conditionQualifiedByName which are the qualifiers for a condition.

Methods annotated with @Condition can have similar parameters as the lifecycle @AfterMapping and @BeforeMapping injected.

@d-tchmnt
Copy link

Hello filiphr,
when should we expect the new functionality to be used?
thanks for all the good work :)

@HemalathaGowdar
Copy link

@filiphr : Could you please help me , I have been using @condition method as suggested below link
https://mapstruct.org/documentation/dev/reference/html/#conditional-mapping

But am not seeing this method has been used/invoked in the generated impl class. Could you please assist.

@filiphr
Copy link
Member

filiphr commented Dec 13, 2021

@HemalathaGowdar you created #2688, there is no need to ping people and comment the some things in multiple issues. I've replied to you in #2688

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants