Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Introduction

MapStruct is an efficient, type-safe library that simplifies data mapping between Java objects, eliminating the need for manual conversion logic.

In this tutorial, we’ll explore the use of MapStruct to map an enum to a string.

2. Mapping an Enum to a String

Using Java enums as strings rather than ordinals simplifies data exchange with external APIs, making data retrieval easier and enhancing readability in a UI.

Suppose that we want to convert the DayOfWeek enum to a string.

DayOfWeek is an enum in the Java Date-Time API that represents the seven days of the week, from Monday to Sunday.

Let’s implement the MapStruct mapper:

@Mapper
public interface DayOfWeekMapper {
    DayOfWeekMapper INSTANCE = Mappers.getMapper(DayOfWeekMapper.class);

    String toString(DayOfWeek dayOfWeek);

    // additional mapping methods as needed
}

The DayOfWeekMapper interface is a MapStruct mapper, designated by @Mapper. We define the toString() method that accepts a DayOfWeek enum and converts it into a string representation. By default, MapStruct uses the name() method to get string values for enums:

class DayOfWeekMapperUnitTest {
    private DayOfWeekMapper dayOfWeekMapper = DayOfWeekMapper.INSTANCE;

    @ParameterizedTest
    @CsvSource({"MONDAY,MONDAY", "TUESDAY,TUESDAY", "WEDNESDAY,WEDNESDAY", "THURSDAY,THURSDAY",
                "FRIDAY,FRIDAY", "SATURDAY,SATURDAY", "SUNDAY,SUNDAY"})
    void whenDayOfWeekMapped_thenGetsNameString(DayOfWeek source, String expected) {
        String target = dayOfWeekMapper.toString(source);
        assertEquals(expected, target);
    }
}

This verifies that toString() maps DayOfWeek enum values to their expected string names. This style of parameterized test also lets us test all possible mutations from a single test.

2.1. Handling null

Now, let’s look at how MapStruct handles null. By default, MapStruct maps a null source to a null target. However, this behavior can be modified.

Straightaway, let’s verify that the mapper returns a null result for a null input:

@Test
void whenNullDayOfWeekMapped_thenGetsNullResult() {
    String target = dayOfWeekMapper.toString(null);
    assertNull(target);
}

MapStruct provides MappingConstants.NULL to manage null values:

@Mapper
public interface DayOfWeekMapper {

    @ValueMapping(target = "MONDAY", source = MappingConstants.NULL)
    String toStringWithDefault(DayOfWeek dayOfWeek);
}

For a null value, this mapping returns the default value MONDAY:

@Test
void whenNullDayOfWeekMappedWithDefaults_thenReturnsDefault() {
    String target = dayOfWeekMapper.toStringWithDefault(null);
    assertEquals("MONDAY", target);
}

3. Mapping a String to an Enum

Now, let’s look at a mapper method to convert the strings back to enums:

@Mapper
public interface DayOfWeekMapper {

    DayOfWeek nameStringToDayOfWeek(String day);
}

This mapper converts a string representing a day of the week into the corresponding DayOfWeek enum value:

@ParameterizedTest
@CsvSource(
        {"MONDAY,MONDAY", "TUESDAY,TUESDAY", "WEDNESDAY,WEDNESDAY", "THURSDAY,THURSDAY",
         "FRIDAY,FRIDAY", "SATURDAY,SATURDAY", "SUNDAY,SUNDAY"})
void whenNameStringMapped_thenGetsDayOfWeek(String source, DayOfWeek expected) {
    DayOfWeek target = dayOfWeekMapper.nameStringToDayOfWeek(source);
    assertEquals(expected, target);
}

We verify that nameStringToDayOfWeek() maps a day’s string representation to its corresponding enum.

3.1. Handling Unmapped Values

MapStruct throws an error if a string does not match the enum name or another constant via @ValueMapping. Generally, this is done to ensure that all values are mapped safely and predictably. The mapping method created by MapStruct throws an IllegalStateException if an unrecognized source value occurs:

@Test
void whenInvalidNameStringMapped_thenThrowsIllegalArgumentException() {
    String source = "Mon";
    IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
        dayOfWeekMapper.nameStringToDayOfWeek(source);
    });
    assertTrue(exception.getMessage().equals("Unexpected enum constant: " + source));
}

To change this behavior, MapStruct also provides MappingConstants.ANY_UNMAPPED. This instructs MapStruct to map any unmapped source values to the target constant values:

@Mapper
public interface DayOfWeekMapper {

    @ValueMapping(target = "MONDAY", source = MappingConstants.ANY_UNMAPPED)
    DayOfWeek nameStringToDayOfWeekWithDefaults(String day);
}

Finally, this @ValueMapping annotation sets the default behavior for unmapped sources. Thus, any unmapped input defaults to MONDAY:

@ParameterizedTest
@CsvSource({"Mon,MONDAY"})
void whenInvalidNameStringMappedWithDefaults_thenReturnsDefault(String source, DayOfWeek expected) {
    DayOfWeek target = dayOfWeekMapper.nameStringToDayOfWeekWithDefaults(source);
    assertEquals(expected, target);
}

4. Mapping an Enum to a Custom String

Now, let’s also convert the enum into a custom short representation of DayOfWeek like “Mon”, “Tue”, and so on:

@Mapper
public interface DayOfWeekMapper {

    @ValueMapping(target = "Mon", source = "MONDAY")
    @ValueMapping(target = "Tue", source = "TUESDAY")
    @ValueMapping(target = "Wed", source = "WEDNESDAY")
    @ValueMapping(target = "Thu", source = "THURSDAY")
    @ValueMapping(target = "Fri", source = "FRIDAY")
    @ValueMapping(target = "Sat", source = "SATURDAY")
    @ValueMapping(target = "Sun", source = "SUNDAY")
    String toShortString(DayOfWeek dayOfWeek);
}

Conversely, this toShortString() mapping configuration uses @ValueMapping to convert DayOfWeek enums to abbreviated strings:

@ParameterizedTest
@CsvSource(
        {"MONDAY,Mon", "TUESDAY,Tue", "WEDNESDAY,Wed", "THURSDAY,Thu",
         "FRIDAY,Fri", "SATURDAY,Sat", "SUNDAY,Sun"})
void whenDayOfWeekMapped_thenGetsShortString(DayOfWeek source, String expected) {
    String target = dayOfWeekMapper.toShortString(source);
    assertEquals(expected, target);
}

5. Mapping a Custom String to an Enum

Finally, we’ll see how to convert the abbreviated string into the DayOfWeek enum:

@Mapper
public interface DayOfWeekMapper {

    @InheritInverseConfiguration(name = "toShortString")
    DayOfWeek shortStringToDayOfWeek(String day);
}

Furthermore, the @InheritInverseConfiguration annotation defines a reverse mapping, which allows shortStringToDayOfWeek() to inherit its configuration from the toShortString() method, converting abbreviated day names to corresponding DayOfWeek enums:

@ParameterizedTest
@CsvSource(
        {"Mon,MONDAY", "Tue,TUESDAY", "Wed,WEDNESDAY", "Thu,THURSDAY",
         "Fri,FRIDAY", "Sat,SATURDAY", "Sun,SUNDAY"})
void whenShortStringMapped_thenGetsDayOfWeek(String source, DayOfWeek expected) {
    DayOfWeek target = dayOfWeekMapper.shortStringToDayOfWeek(source);
    assertEquals(expected, target);
}

6. Conclusion

In this article, we learned how to map enum-to-string with MapStruct’s @ValueMapping annotation.  We also used @InheritInverseConfiguration to keep things consistent when mapping back and forth. Using these tricks, we smoothly tackle enum-to-string conversions and keep our code clear and easy to maintain.

As always, the complete code samples for this article can be found over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments