Mapping from class with non-JavaBean style getters

723 views
Skip to first unread message

Scott More

unread,
Nov 27, 2014, 1:08:49 AM11/27/14
to mapstru...@googlegroups.com
Hello!

Thanks for developing MapStruct, it's a great idea!  There is just one remaining scenario I am trying to figure out:

There are cases where I need to map from Java classes that don't follow the getFoo() method JavaBeans naming convention, instead consistently lobbing off the "get" part.  For example, can I automatically map from the following class to a class with matching "setName(String)" setter without having to define mappings explicitly (i.e via configuration annotation or similar)?

public class SourceClass {

    private String name;

    public
SourceClass() {
    }

    public String name() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


Thanks,
Scott

Gunnar Morling

unread,
Nov 27, 2014, 2:53:25 AM11/27/14
to Scott More, mapstru...@googlegroups.com
Hi Scott,

2014-11-27 7:08 GMT+01:00 Scott More <sc...@tilialabs.com>:
Hello!

Thanks for developing MapStruct, it's a great idea!

Thanks for the nice words; great to hear you find it useful!

  There is just one remaining scenario I am trying to figure out:

There are cases where I need to map from Java classes that don't follow the getFoo() method JavaBeans naming convention, instead consistently lobbing off the "get" part.  For example, can I automatically map from the following class to a class with matching "setName(String)" setter without having to define mappings explicitly (i.e via configuration annotation or similar)?

No, that's not possible. Property getters and setters must follow the JavaBeans spec to be considered by MapStruct (with the exception that setters don't have to be void necessarily, which will be supported by the upcoming Beta3 release in order to allow for fluent-style setter invocations).

I don't think we should relax these rules by default. Otherwise it might happen that an action method rather than a "getter" gets invoked within the course of mapping. We couldn't distinguish that "name()" represents a getter, whereas "selfdestruct()" doesn't.

That being said, the property selection is driven by the target bean, so there would have to be "setSelfdestruct()" in the target bean to trigger that getter in the source bean. Based on that, we may consider to provide some configuration option which allows you to enable this more relaxed getter handling, with the described risks.

WDYT?

Hth,

--Gunnar
 

public class SourceClass {

    private String name;

    public
SourceClass() {
    }

    public String name() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


Thanks,
Scott

--
You received this message because you are subscribed to the Google Groups "mapstruct-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mapstruct-use...@googlegroups.com.
To post to this group, send email to mapstru...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Scott More

unread,
Nov 27, 2014, 3:14:25 AM11/27/14
to mapstru...@googlegroups.com
Hi Gunnar,

Thanks for the quick reply.  I agree relaxing the rules by default is not a good idea.  Adding a configuration option to enable relaxed getter handling would be fantastic and most likely sufficient for everything I need to do.  It might be overkill, but one way to handle all kinds of odd getter naming conventions explicitly is to provide a way to define custom rules programmatically, something like the NameTransformer + NamingConvention interfaces in ModelMapper.

Thanks,
Scott

Gunnar Morling

unread,
Nov 27, 2014, 4:19:02 AM11/27/14
to Scott More, mapstru...@googlegroups.com
Hi,

2014-11-27 9:14 GMT+01:00 Scott More <sc...@tilialabs.com>:
Hi Gunnar,

Thanks for the quick reply.  I agree relaxing the rules by default is not a good idea.  Adding a configuration option to enable relaxed getter handling would be fantastic and most likely sufficient for everything I need to do.  It might be overkill, but one way to handle all kinds of odd getter naming conventions explicitly is to provide a way to define custom rules programmatically, something like the NameTransformer + NamingConvention interfaces in ModelMapper.

Making the naming strategy pluggable seems interesting. Could you provide an example for how this might look like?


Thanks,
Scott

--Gunnar
 
On Thursday, November 27, 2014 3:08:49 PM UTC+9, Scott More wrote:
Hello!

Thanks for developing MapStruct, it's a great idea!  There is just one remaining scenario I am trying to figure out:

There are cases where I need to map from Java classes that don't follow the getFoo() method JavaBeans naming convention, instead consistently lobbing off the "get" part.  For example, can I automatically map from the following class to a class with matching "setName(String)" setter without having to define mappings explicitly (i.e via configuration annotation or similar)?

public class SourceClass {

    private String name;

    public
SourceClass() {
    }

    public String name() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


Thanks,
Scott

--

Scott More

unread,
Nov 27, 2014, 6:08:59 AM11/27/14
to mapstru...@googlegroups.com, sc...@tilialabs.com
Hi Gunnar,

One idea is to introduce an interface to map from getter method to property name, then have an optional argument on the configure annotation to specify a class implementing that interface.  The mapper could map method names in source class to property names (JavaBeans: getName -> name, My example: name -> name).  Hopefully that would play nice with the @Mapper annotations and allows for a very similar SetterTransform to map from property name to any bizarre setter method convention in the target class.

something like this:

    public interface GetterTransform {
       
        /**
         * Transform method receives all method names in source class of
         * no-argument, non-void return type methods and returns property
         * name or null if method is not to be mapped
         *
         * @param methodName Name of potential getter method in source class
         * @return Expected property name in target class or null to skip
         */
        public String transform(String methodName);
    }


A JavaBeans-compliant class transform would look something like this:
   
    public class JavaBeansTransform implements GetterTransform {

        public String transform(String methodName) {
            if (!methodName.startsWith("get") || methodName.length() <= 3) {
                return null;
            }
           
            // Lob off get and lowercase next character
            return String.format("%s%s",
                    methodName.substring(3, 4).toLowerCase(),
                    methodName.substring(4));
        }
    }

   
My example above would look like this:

    public class CustomTransform implements GetterTransform {
       
        public String transform(String methodName) {
            return methodName;
        }
    }


Thanks,
Scott

Gunnar Morling

unread,
Nov 27, 2014, 6:36:58 AM11/27/14
to Scott More, mapstru...@googlegroups.com
Yeah, that looks promising.

Could you open an issue in our tracker (https://github.com/mapstruct/mapstruct/issues) for it, capturing the discussion?

One minor issue I see with the proposal of

    @Mapper(propertyNamer=MyTransformer.class)

is that it requires a class which actually only would be needed for the generation (MyTransformer) to be visible to the application; javac has a separation of the compilation classpath and the "processor path" which contains the classes required by annotation processing. Not sure whether it's really a problem; One could work around it by e.g. specifying the FQN instead (we have a set of processor options already).

Thanks,

--Gunnar

Scott More

unread,
Nov 27, 2014, 8:27:03 PM11/27/14
to mapstru...@googlegroups.com, sc...@tilialabs.com
Sounds good.  I opened an issue on Github:

https://github.com/mapstruct/mapstruct/issues/365

Let me know if you need a different approach due to problems in the annotation processor context or other, I'm happy to try to come up with something different.  I might have a few cycles to help on implementation or testing side too if that makes sense.

Thanks,
Scott

Gunnar Morling

unread,
Nov 28, 2014, 4:47:22 PM11/28/14
to mapstru...@googlegroups.com, sc...@tilialabs.com
On Friday, November 28, 2014 2:27:03 AM UTC+1, Scott More wrote:
Sounds good.  I opened an issue on Github:

https://github.com/mapstruct/mapstruct/issues/365

Great, thanks! 

Let me know if you need a different approach due to problems in the annotation processor context or other, I'm happy to try to come up with something different.  I might have a few cycles to help on implementation or testing side too if that makes sense.

Sure, any help is welcome. Would you like to take the lead for building this feature?

There are some references at http://mapstruct.org/contributing/#section-03 which should you get started with hacking on MapStruct. The getter/setter handling currently is hard-wired in the Executables class, that'd be the point where we'd have to make things customizable.

Thanks,
Scott

Cheers,

Scott More

unread,
Dec 2, 2014, 1:20:25 AM12/2/14
to mapstru...@googlegroups.com, sc...@tilialabs.com
No problem.  I did some hacking late in the week and got Executables doing the loose mapping correctly with non get-prefixed methods (e.g. foo() -> foo).  So far everything seems straightforward in the code and build env.

Your comments on issue #365 all make sense.  I do have one question:  Would mapstruct-processor be dependent on the new mapstruct.spi artifact/jar?  If not, how would you like that to work?

One simple way to give access to the default implementation is to have that default class in the mapstruct.spi artifact so users can just extend if desired.

I'm happy to take lead on this if that makes sense.  My schedule is pretty tight for the next two weeks at least but I should have some time here and there to start in on a proper solution.

Thanks,
Scott

Gunnar Morling

unread,
Dec 2, 2014, 4:16:04 PM12/2/14
to mapstru...@googlegroups.com, sc...@tilialabs.com
Hi,

On Tuesday, December 2, 2014 7:20:25 AM UTC+1, Scott More wrote:
No problem.  I did some hacking late in the week and got Executables doing the loose mapping correctly with non get-prefixed methods (e.g. foo() -> foo).  So far everything seems straightforward in the code and build env. 

Cool, glad to hear that. 

Your comments on issue #365 all make sense.  I do have one question:  Would mapstruct-processor be dependent on the new mapstruct.spi artifact/jar?  If not, how would you like that to work?

Yes, I think that dependency would be required. I don't think we could otherwise instantiate the custom property name handler from within the processor.

One simple way to give access to the default implementation is to have that default class in the mapstruct.spi artifact so users can just extend if desired.

That, or some kind of delegation. 

I'm happy to take lead on this if that makes sense.  My schedule is pretty tight for the next two weeks at least but I should have some time here and there to start in on a proper solution.

Absolutely, your help would be more than welcome! No worries about the schedule, with the Beta3 release just through the door, we have some time until the next release.

Thanks,
Scott

Thank you,

--Gunnar
Reply all
Reply to author
Forward
0 new messages