Mapping Strings to Enums

5,636 views
Skip to first unread message

Charlie Stear

unread,
May 8, 2014, 3:46:09 PM5/8/14
to mapstru...@googlegroups.com
It took some time for me to figure out how to get mapping from strings to enums to work, but I got it working and thought I'd share my experience.

First off, I did have some difficulty getting MapStruct working within IntelliJ but I eventually got it working simply by upgrading to the latest build (13.1.2).  After that, the IDE had no problems locating the annotations and generating the mapper implementations.

MapStruct by convention mapped Strings to Enums if the String value exactly matched the Enum value as a string.  But in my case, the source string is different than the enum value, like:

source.color = "Light Blue"
target.color = ColorEnum.LT_BLUE

So, my first attempt was to use the Mappings annotation to map the source string to the destination enum's string value, like this:

    @Mappings({
            @Mapping(source = "Light Blue", target = "LT_BLUE"),
            ...
    })
    ColorEnum colorToColor(String color);

But the compiler complained saying "Can't generate mapping method from non-enum type to enum type."  

So, next I tried creating my own implementation.  I didn't see any documentation on creating custom mapper implementations, but I thought I'd try it out.  I made the enum mapper a class instead of an interface and wrote the code to map from the string to enum.  I took out the @Mapper and @Mappings annotations and the INSTANCE member variable, and added to the parent class the 'uses' annotation parameter.  And it worked!   The parent mapper was able to locate the enum mapper class and use it.

This required me to define the mappings in a non-annotation way.  I chose to put them in separate class.  It would have been nice for MapStruct to support string to enum by specifying the @Mappings annotation like above.  But I'm not sure how often my use case comes up.  Another possible use case which I think would come up often is mapping numerics to enums, and not simply numerics representing an ordinal position but numbers that require an explicit mapping, like:

    @Mappings({
            @Mapping(source = "Light Blue", target = 1),
                            ...
    })
    ColorEnum colorToColor(Integer colorId);

We almost went with our DTO using numerics but decided to use human readable strings instead since the source is actually json.  I didn't try this but I assume this would also generate the "Can't generate..to enum type" message.  Just a few suggestions, but being able to define and reference explicit mappers is a reasonable solution.

Thanks!
Charlie

Gunnar Morling

unread,
May 9, 2014, 10:34:06 AM5/9/14
to Charlie Stear, mapstru...@googlegroups.com
Hi Charlie,

2014-05-08 21:46 GMT+02:00 Charlie Stear <cst...@gmail.com>:
It took some time for me to figure out how to get mapping from strings to enums to work, but I got it working and thought I'd share my experience.

Thanks for getting in touch and sharing your findings; That's much appreciated!

First off, I did have some difficulty getting MapStruct working within IntelliJ but I eventually got it working simply by upgrading to the latest build (13.1.2).  After that, the IDE had no problems locating the annotations and generating the mapper implementations.

MapStruct by convention mapped Strings to Enums if the String value exactly matched the Enum value as a string.  But in my case, the source string is different than the enum value, like:

source.color = "Light Blue"
target.color = ColorEnum.LT_BLUE

So, my first attempt was to use the Mappings annotation to map the source string to the destination enum's string value, like this:

    @Mappings({
            @Mapping(source = "Light Blue", target = "LT_BLUE"),
            ...
    })
    ColorEnum colorToColor(String color);

But the compiler complained saying "Can't generate mapping method from non-enum type to enum type."  

So, next I tried creating my own implementation.  I didn't see any documentation on creating custom mapper implementations,

Actually that's covered in section 5.3 of the reference documentation (http://mapstruct.org/documentation/#section-05-03). There as an example, just let me know in case you think anything more should be added to the docs.
 
but I thought I'd try it out.  I made the enum mapper a class instead of an interface and wrote the code to map from the string to enum.  I took out the @Mapper and @Mappings annotations and the INSTANCE member variable, and added to the parent class the 'uses' annotation parameter.  And it worked!   The parent mapper was able to locate the enum mapper class and use it.

Nice. Btw. you also could make your MapStruct-generated mapper an abstract class instead of an interface and define that custom method directly there. But it probably makes more sense to have it on a separate class when it's going to be re-used.

This required me to define the mappings in a non-annotation way.  I chose to put them in separate class.  It would have been nice for MapStruct to support string to enum by specifying the @Mappings annotation like above.  But I'm not sure how often my use case comes up.  Another possible use case which I think would come up often is mapping numerics to enums, and not simply numerics representing an ordinal position but numbers that require an explicit mapping, like:

    @Mappings({
            @Mapping(source = "Light Blue", target = 1),
                            ...
    })
    ColorEnum colorToColor(Integer colorId);

We almost went with our DTO using numerics but decided to use human readable strings instead since the source is actually json.  I didn't try this but I assume this would also generate the "Can't generate..to enum type" message.  Just a few suggestions, but being able to define and reference explicit mappers is a reasonable solution.

I feel that implementing these mappings via a custom method is indeed the better approach. Taking the numeric case, there is no generally sensible default mapping (using the ordinal value would not be recommendable), so one would have to configure all values explicitly anyways. I don't think much would be gained by doing this via annotations instead of a hand-written method. And as you say it's very easy to hook in a manually implemented method.
 
Thanks!
Charlie

Thanks again for your feedback,

--Gunnar
 

--
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.

Reply all
Reply to author
Forward
0 new messages