1. Introduction
An empty string (“”) is the String object with an intentional blank value. Null is a literal used in Java to denote the absence of an object and is the default value for the reference type object. Null typically means “no value” or “unknown”. In many cases, these two should be treated differently. However, if an empty string does not carry any meaningful distinction, converting it to null helps reduce ambiguity. In this example, I will demonstrate MapStruct empty String null conversion.
2. Setup
In this step, I will create a Gradle project along with Lombok and MapStruct libraries.
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.5'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.zheng.mapstruct.demo'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
3. Java POJO
3.1 Source Class
In this step, I will create a Source.java that has two String fields: field1 and field2.
Source.java
package com.zheng.mapstruct.demo.data;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class Source {
private String field1;
private String field2;
}
3.2 Target Class
In this step, I will create a Target.java that has two String fields: field1 and field2.
Target.java
package com.zheng.mapstruct.demo.data;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class Target {
private String field1;
private String field2;
}
4. Mapstruct Empty String Null Mapper Interface
In this step, I will create several Mapper Interfaces that map the Source to Target and map empty String to null:
MapperWithCondition: mapStringtonullbased on theisNotEmptycondition. It applies to allStringfields satisfying the condition.MapperWithGlobalMethod: mapStringtonullbased on thedefaultemptyStringToNullmethod. It applies to allStringfields.MapperWithExpression: mapStringtonullbased on thejava(source.getField2() != null && !source.getField2().trim().isEmpty() ? source.getField2() : null)expression. It only applies tofield2annotated with@Mapping.MapperWithQualifiedByName: mapStringtonullbased on thequalifiedByName="emptyToNull"attribute. It only applies tofield2annotated with@Mapping. TheemptyToNullmethod is defined within the class.MapperWithExternalQualifiedByName: mapStringtonullbased on thequalifiedByName="emptyStringToNull"attribute. It only applies tofield2annotated with@Mapping. TheemptyStringToNullmethod is defined inStringTransformUtils.class.MapperWithQualifiedBy: mapStringtonullbased on thequalifiedBy = EmptyStringToNull.classattribute. It only applies tofield2annotated with@Mapping.
4.1 Mapstruct Empty String Null via Default Method for All String
In this step, I will create a MapperWithGlobalMethod to map String to null based on the default emptyStringToNull method. It applies to all String fields.
MapperWithGlobalMethod.java
package com.zheng.mapstruct.demo.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
@Mapper
public interface MapperWithGlobalMethod {
MapperWithGlobalMethod INSTANCE = Mappers.getMapper(MapperWithGlobalMethod.class);
default String emptyStringToNull(String value) {
return (value == null || value.trim().isEmpty()) ? null : value;
}
Target map(Source source);
}
- Line 13: the
defaultmethod to map an empty string tonull.
4.2 Mapstruct Empty String Null via @Condition Method for All String
In this step, I will create a MapperWithCondition to map String to null based on the default isNotEmpty condition method. It applies to all String fields.
MapperWithCondition.java
package com.zheng.mapstruct.demo.mapper;
import org.mapstruct.Condition;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
@Mapper
public interface MapperWithCondition {
MapperWithCondition INSTANCE = Mappers.getMapper(MapperWithCondition.class);
@Condition
default boolean isNotEmpty(String value) {
return value != null && !value.trim().isEmpty();
}
Target map(Source source);
}
- Line 14:
@Conditionannotation is added. - Line 15: the
defaultmethod to convert an empty string tonull.
4.3 Mapstruct Empty String Null via Expression
In this step, I will create a MapperWithExpression to map String to null based on the @Mapping‘s expression attribute. It applies to the fields annotated with @Mapping fields.
MapperWithExpression.java
package com.zheng.mapstruct.demo.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
@Mapper
public interface MapperWithExpression {
MapperWithExpression INSTANCE = Mappers.getMapper(MapperWithExpression.class);
@Mapping(target = "field2", expression = "java(source.getField2() != null && !source.getField2().trim().isEmpty() ? source.getField2() : null)")
Target map(Source source);
}
- Line 14: an
expressionis added to convert an empty string tonull.
4.4 Mapstruct Empty String Null via QualifiedByName
In this step, I will create a MapperWithQualifiedByName to map String to null based on the static emptyToNull method annotated with @Named("emptyToNull"). It applies to the fields annotated with @Mapping.
MapperWithQualifiedByName.java
package com.zheng.mapstruct.demo.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
@Mapper
public interface MapperWithQualifiedByName {
MapperWithQualifiedByName INSTANCE = Mappers.getMapper(MapperWithQualifiedByName.class);
@Named("emptyToNull")
static String emptyToNull(String value) {
return (value == null || value.trim().isEmpty()) ? null : value;
}
@Mapping(target = "field2", qualifiedByName = "emptyToNull")
Target map(Source source);
}
- Line 15: annotate with
@Named("emptyToNull"). - Line 16: a
staticmethod to convert an empty string tonull. - Line 20: add the
qualifiedByNameattribute for the@Mapping.
4.5 Mapstruct Empty String Null via QualifiedByName Externally
In this step, I will create a MapperWithExternalQualifiedByName that maps String to null based on a static emptyStringToNull method annotated with @Named("emptyToNull") and defined externally in StringTransformUtils.class. It applies to the fields annotated with @Mapping.
MapperWithExternalQualifiedByName.java
package com.zheng.mapstruct.demo.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
@Mapper(uses = StringTransformUtils.class)
public interface MapperWithExternalQualifiedByName {
MapperWithExternalQualifiedByName INSTANCE = Mappers.getMapper(MapperWithExternalQualifiedByName.class);
@Mapping(source = "field2", target = "field2", qualifiedByName = "emptyStringToNull")
Target emptyToNullMapper(Source src);
Target map(Source src);
}
- Line 10: uses the
StringTransformUtilsclass. - Line 14: set “
emptyStringToNull” for thequalifiedByNameattribute.
StringTransformUtils.java
package com.zheng.mapstruct.demo.mapper;
import org.mapstruct.Named;
@Named("emptyStringToNull")
public class StringTransformUtils {
public static String emptyStringToNull(String val) {
return (val == null || val.trim().isEmpty()) ? null : val;
}
}
- Line 5, annotated
@Namedfor theemptyStringToNull. - Line 7: define a
staticmethod to convert an empty string tonull.
4.6 Mapstruct Empty String Null via QualifiedBy
In this step, I will create a MapperWithQualifiedBy that maps String to null based on a default emptyStringToNull method annotated with customized @EmptyStringToNull. It applies to the fields annotated with @Mapping.
MapperWithQualifiedBy.java
package com.zheng.mapstruct.demo.mapper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Qualifier;
import org.mapstruct.factory.Mappers;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
@Mapper
public interface MapperWithQualifiedBy {
@Qualifier
@java.lang.annotation.Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface EmptyStringToNull {
}
MapperWithQualifiedBy INSTANCE = Mappers.getMapper(MapperWithQualifiedBy.class);
@EmptyStringToNull
default String emptyStringToNull(String s) {
return s.isEmpty() ? null : s;
}
@Mapping(target = "field2", qualifiedBy = EmptyStringToNull.class)
Target map(Source source);
}
- Line 20: define a customized annotation.
- Line 25, 26: define a
defaultmethod to convert an empty string tonull. - Line 30: add the
qualifiedByattribute to@Mapping.
5. Generated Mapper Implementation Class
5.1 MapperWithGlobalMethodImpl
In this step, I will view the generated mapper implementation class: MapperWithGlobalMethodImpl.
MapperWithGlobalMethodImpl.java
package com.zheng.mapstruct.demo.mapper;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
import javax.annotation.processing.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2025-05-02T21:28:44-0500",
comments = "version: 1.5.5.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-8.13.jar, environment: Java 17 (Oracle Corporation)"
)
public class MapperWithGlobalMethodImpl implements MapperWithGlobalMethod {
@Override
public Target map(Source source) {
if ( source == null ) {
return null;
}
Target target = new Target();
target.setField1( emptyStringToNull( source.getField1() ) );
target.setField2( emptyStringToNull( source.getField2() ) );
return target;
}
}
- Line 22, 23: Both
field1andfield2are mapped byemptyStringToNull.
5.2 MapperWithConditionImpl
In this step, I will view the generated mapper implementation class: MapperWithConditionImpl.
MapperWithConditionImpl.java
package com.zheng.mapstruct.demo.mapper;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
import javax.annotation.processing.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2025-05-02T21:28:44-0500",
comments = "version: 1.5.5.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-8.13.jar, environment: Java 17 (Oracle Corporation)"
)
public class MapperWithConditionImpl implements MapperWithCondition {
@Override
public Target map(Source source) {
if ( source == null ) {
return null;
}
Target target = new Target();
if ( isNotEmpty( source.getField1() ) ) {
target.setField1( source.getField1() );
}
if ( isNotEmpty( source.getField2() ) ) {
target.setField2( source.getField2() );
}
return target;
}
}
- Line 22, 25: an
if isNotEmptycondition is checked.
5.3 MapperWithExpressionImpl
In this step, I will view the generated mapper implementation class: MapperWithExpressionImpl.
MapperWithExpressionImpl.java
package com.zheng.mapstruct.demo.mapper;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
import javax.annotation.processing.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2025-05-02T21:28:44-0500",
comments = "version: 1.5.5.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-8.13.jar, environment: Java 17 (Oracle Corporation)"
)
public class MapperWithExpressionImpl implements MapperWithExpression {
@Override
public Target map(Source source) {
if ( source == null ) {
return null;
}
Target target = new Target();
target.setField1( source.getField1() );
target.setField2( source.getField2() != null && !source.getField2().trim().isEmpty() ? source.getField2() : null );
return target;
}
}
- Line 24: the
field2setter applies theexpressiondefined in@Mapping.
5.4 MapperWithQualifiedByNameImpl
In this step, I will view the generated mapper implementation class: MapperWithQualifiedByNameImpl.
MapperWithQualifiedByNameImpl.java
package com.zheng.mapstruct.demo.mapper;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
import javax.annotation.processing.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2025-05-02T21:28:44-0500",
comments = "version: 1.5.5.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-8.13.jar, environment: Java 17 (Oracle Corporation)"
)
public class MapperWithQualifiedByNameImpl implements MapperWithQualifiedByName {
@Override
public Target map(Source source) {
if ( source == null ) {
return null;
}
Target target = new Target();
target.setField2( MapperWithQualifiedByName.emptyToNull( source.getField2() ) );
target.setField1( source.getField1() );
return target;
}
}
- Line 22: the
field2setter applies thestaticMapperWithQualifiedByName.emptyToNull.
5.5 MapperWithExternalQualifiedByNameImpl
In this step, I will view the generated mapper implementation class: MapperWithExternalQualifiedByNameImpl.
MapperWithExternalQualifiedByNameImpl.java
package com.zheng.mapstruct.demo.mapper;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
import javax.annotation.processing.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2025-05-02T21:28:44-0500",
comments = "version: 1.5.5.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-8.13.jar, environment: Java 17 (Oracle Corporation)"
)
public class MapperWithExternalQualifiedByNameImpl implements MapperWithExternalQualifiedByName {
@Override
public Target emptyToNullMapper(Source src) {
if ( src == null ) {
return null;
}
Target target = new Target();
target.setField2( StringTransformUtils.emptyStringToNull( src.getField2() ) );
target.setField1( src.getField1() );
return target;
}
@Override
public Target map(Source src) {
if ( src == null ) {
return null;
}
Target target = new Target();
target.setField1( src.getField1() );
target.setField2( src.getField2() );
return target;
}
}
- Line 22: the
field2setter uses thestatic StringTransformUtils.emptyStringToNull.
5.6 MapperWithQualifiedByImpl
In this step, I will view the generated mapper implementation class: MapperWithQualifiedByImpl.
MapperWithQualifiedByImpl.java
package com.zheng.mapstruct.demo.mapper;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
import javax.annotation.processing.Generated;
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2025-05-02T21:28:44-0500",
comments = "version: 1.5.5.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-8.13.jar, environment: Java 17 (Oracle Corporation)"
)
public class MapperWithQualifiedByImpl implements MapperWithQualifiedBy {
@Override
public Target map(Source source) {
if ( source == null ) {
return null;
}
Target target = new Target();
target.setField2( emptyStringToNull( source.getField2() ) );
target.setField1( source.getField1() );
return target;
}
}
- Line 22: the
field2setter applies via theemptyStringToNull.
6. Test Mapstruct Empty String Null
6.1 TestBase
In this step, I will create TestBase.java that defines the source and target variables and setupFields method to prepare the test data.
TestBase.java
package com.zheng.mapstruct.demo.mapper;
import com.zheng.mapstruct.demo.data.Source;
import com.zheng.mapstruct.demo.data.Target;
public class TestBase {
protected Source source;
protected Target target;
public TestBase() {
super();
}
protected void setupFields(String field1, String field2) {
source = new Source();
source.setField1(field1);
source.setField2(field2);
}
}6.2 MapperWithGlobalMethodTest
In this step, I will create a MapperWithGlobalMethodTest.java that verifies all the empty string fields are converted to null.
MapperWithGlobalMethodTest.java
package com.zheng.mapstruct.demo.mapper;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
class MapperWithGlobalMethodTest extends TestBase {
private MapperWithGlobalMethod testClass = MapperWithGlobalMethod.INSTANCE;
@Test
void test_Field2_emptyToNull() {
setupFields("", "");
target = testClass.map(source);
assertNull(target.getField1());
assertNull(target.getField2());
}
@Test
void test_Field1_still_empty() {
setupFields("T", "");
target = testClass.map(source);
assertEquals("T", target.getField1());
assertNull(target.getField2());
}
}
6.3 MapperWithConditionTest
In this step, I will create a MapperWithConditionTest.java that verifies all the empty string fields are converted to null.
MapperWithConditionTest.java
package com.zheng.mapstruct.demo.mapper;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
class MapperWithConditionTest extends TestBase {
private MapperWithCondition testClass = MapperWithCondition.INSTANCE;
@Test
void test_emptyToNull() {
setupFields("", "");
target = testClass.map(source);
assertNull(target.getField1());
assertNull(target.getField2());
}
@Test
void test_nonEmpty_is_mapped() {
setupFields("T", "");
target = testClass.map(source);
assertEquals("T", target.getField1());
assertNull(target.getField2());
}
}
6.4 MapperWithExpressionTest
In this step, I will create a MapperWithExpressionTest.java that verifies an empty string at field2 is converted to null.
MapperWithExpressionTest.java
package com.zheng.mapstruct.demo.mapper;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
class MapperWithExpressionTest extends TestBase {
private MapperWithExpression testClass = MapperWithExpression.INSTANCE;
@Test
void test_Field2_emptyToNull() {
setupFields("T", "");
target = testClass.map(source);
assertEquals("T", target.getField1());
assertNull(target.getField2());
}
@Test
void test_Field1_remain_empty() {
setupFields("", "");
target = testClass.map(source);
assertEquals("", target.getField1());
assertNull(target.getField2());
}
}
6.5 MapperWithQualifiedByName
In this step, I will create a MapperWithQualifiedByName.java that verifies an empty string at field2 is converted to null.
MapperWithQualifiedByName.java
package com.zheng.mapstruct.demo.mapper;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
class MapperWithQualifiedByNameTest extends TestBase {
private MapperWithQualifiedByName testClass = MapperWithQualifiedByName.INSTANCE;
@Test
void test_Field2_emptyToNull() {
setupFields("", "");
target = testClass.map(source);
assertEquals("", target.getField1());
assertNull(target.getField2());
}
@Test
void test_Field1_still_empty() {
setupFields("T", "");
target = testClass.map(source);
assertEquals("T", target.getField1());
assertNull(target.getField2());
}
}
6.6 MapperWithExternalQualifiedByNameTest
In this step, I will create a MapperWithExternalQualifiedByNameTest.java that verifies an empty string at field2 is converted to null.
MapperWithExternalQualifiedByNameTest.java
package com.zheng.mapstruct.demo.mapper;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
class MapperWithExternalQualifiedByNameTest extends TestBase {
private MapperWithExternalQualifiedByName testClass = MapperWithExternalQualifiedByName.INSTANCE;
@Test
void test_Field2_emptyToNull() {
setupFields("", "");
target = testClass.emptyToNullMapper(source);
assertEquals("", target.getField1());
assertNull(target.getField2());
}
@Test
void test_normalmap_remain_empty() {
setupFields("T", "");
target = testClass.map(source);
assertEquals("T", target.getField1());
assertEquals("", target.getField2());
}
}
6.7 MapperWithQualifiedByTest
In this step, I will create a MapperWithQualifiedByTest.java that verifies an empty string at field2 is converted to null.
MapperWithQualifiedByTest.java
package com.zheng.mapstruct.demo.mapper;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
class MapperWithQualifiedByTest extends TestBase {
private MapperWithQualifiedBy testClass = MapperWithQualifiedBy.INSTANCE;
@Test
void test_Field2_emptyToNull() {
setupFields("", "");
target = testClass.map(source);
assertEquals("", target.getField1());
assertNull(target.getField2());
}
@Test
void test_Field1_still_empty() {
setupFields("T", "");
target = testClass.map(source);
assertEquals("T", target.getField1());
assertNull(target.getField2());
}
}
Run the Junit tests and capture the test results
7. Conclusion
In this example, I demonstrated several ways to convert an empty string to null with MapStruct. The global default method is the simplest way. I outlined the difference in the following table.
Global Default | @Condition | expression | qualifiedByName | qualifiedBy | |
Example | MapperWithGlobalMethod | MapperWithCondition | MapperWithExpression | MapperWithExternalQualifiedByName, MapperWithQualifiedByName | MapperWithQualifiedBy |
Apply to All String fields? | Yes | Yes | No | No | No |
Description | applies to every String→String mapping automatically. | the empty value is skipped during the mapping. | @Named is applied to the emptyToNull method | a customized qualifier annotation EmptyStringToNull is defined | |
Details | @Condition is applied to a default method | expression is one of the @Mapping attributes | qualifiedByName is one of the @Mapping attributes | qualifiedBy is one of the @Mapping attributes | |
When to use? | many String fields | Updating existing objects, preserving old values | One‑off, very custom logic | Few fields & you want clarity | Few fields & you want clarity |
8. Download
This was an example of a Gradle project that used Mapstruct to convert empty strings to nulls.
You can download the full source code of this example here: Map an Empty String to Null Using MapStruct Example


