Java
javaTutorial
Jackson DTO deep copy: non-invasively ignore specific types and their array fields
Jackson DTO deep copy: non-invasively ignore specific types and their array fields

This article explores how to non-intrusively ignore non-serializable fields when doing DTO deep copies with Jackson, especially when those fields are arrays of a specific type. Through `ObjectMapper#addMixIn()` and `@JsonIgnoreType`, types such as `MultipartFile` and its array `MultipartFile[]` can be flexibly excluded, thereby avoiding serialization exceptions and achieving smooth cloning of DTOs. The article also pointed out that the use of generic collections (such as `List
In modern Java application development, especially under the Spring Boot framework, deep copying of data transfer objects (DTO) is a common requirement. For example, when processing web requests, we may need to copy a DTO containing a user-uploaded file (such as MultipartFile) for subsequent processing or state snapshots. However, objects such as MultipartFile are usually non-serializable, and direct cloning using ObjectOutputStream and ObjectInputStream will result in NotSerializableException.
When the DTO class definition cannot be modified (that is, the transient keyword or the @JsonIgnore annotation cannot be added), Jackson's ObjectMapper provides a powerful non-intrusive solution: the Mix-in mechanism .
Overview of Jackson Mix-in Mechanism
Jackson's Mix-in mechanism allows us to add or override Jackson's serialization/deserialization behavior for the target class without modifying the source code. This is accomplished by mapping a "Mix-in" interface or abstract class to the target class. Mix-in classes can contain any Jackson annotations, which will be considered part of the target class.
To ignore a field of a specific type, we can define an empty Mix-in class annotated with @JsonIgnoreType and apply it to the target type. The @JsonIgnoreType annotation instructs Jackson to completely ignore the serialization and deserialization of this type and all its instances.
import com.fasterxml.jackson.annotation.JsonIgnoreType;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.multipart.MultipartFile;
//Define a Mix-in interface for ignoring specific types @JsonIgnoreType
abstract class JacksonMixInForIgnoreType {}
Ignore a single field of a specific type
Suppose our DTO contains a field of type MultipartFile. We can map JacksonMixInForIgnoreType to MultipartFile.class through the ObjectMapper#addMixIn() method.
public class DtoCloner {
private Object makeClone(Object obj) {
ObjectMapper mapper = new ObjectMapper();
//Apply JacksonMixInForIgnoreType to the MultipartFile class so that it is ignored mapper.addMixIn(MultipartFile.class, JacksonMixInForIgnoreType.class);
try {
// First serialize to JSON string, then deserialize back to object to achieve deep copy return mapper.readValue(mapper.writeValueAsString(obj), obj.getClass());
} catch (JsonProcessingException e) {
throw new RuntimeException("DTO cloning failed", e);
}
}
// ...other methods}
This approach works well for situations where a single MultipartFile field is included in the DTO. When Jackson tries to serialize obj and encounters a field of type MultipartFile, it will be ignored directly according to the Mix-in configuration, thus avoiding serialization exceptions.
Solve the problem of ignoring array type fields
However, when the DTO contains fields of type MultipartFile[] (MultipartFile array), the above strategy will fail. When Jackson serializes an array, it attempts to serialize each element in the array. Since we only added a Mix-in to MultipartFile.class and MultipartFile[] itself is a different type (array type), Jackson will try to serialize the MultipartFile instances in the array and still end up throwing an InvalidDefinitionException because it cannot find the serializer for some non-serializable fields inside MultipartFile (such as the FileDescriptor in the inputStream).
To solve this problem, we need to add a Mix-in to the array type itself. This means that if a DTO contains a field like MultipartFile[] fileArray;, we not only have to ignore MultipartFile.class, but also explicitly ignore MultipartFile[].class.
public class DtoCloner {
private Object makeClone(Object obj) {
ObjectMapper mapper = new ObjectMapper();
// Ignore a single MultipartFile type mapper.addMixIn(MultipartFile.class, JacksonMixInForIgnoreType.class);
//Additionally ignore the MultipartFile array type mapper.addMixIn(MultipartFile[].class, JacksonMixInForIgnoreType.class);
try {
return mapper.readValue(mapper.writeValueAsString(obj), obj.getClass());
} catch (JsonProcessingException e) {
throw new RuntimeException("DTO cloning failed", e);
}
}
//Define a Mix-in interface for ignoring specific types @JsonIgnoreType
abstract class JacksonMixInForIgnoreType {}
}
By adding JacksonMixInForIgnoreType to both MultipartFile.class and MultipartFile[].class, Jackson will directly ignore these two types when encountering them, thus successfully achieving deep copying of DTOs.
Notes on collection type fields (such as List)
It is worth noting that this addMixIn strategy does not apply to the direct ignoring of generic collections (such as List
This means that even if you try mapper.addMixIn(List.class, JacksonMixInForIgnoreType.class) this will just ignore all List instances, which is generally not the desired behavior as it will ignore all lists, not just the one containing a MultipartFile. Currently, there is no direct, general way to ignore the collection type of a specific generic parameter via ObjectMapper#addMixIn(). For cases like this, other strategies may need to be considered, such as using @JsonIgnore at the DTO level (if the DTO is modifiable), or writing custom serializers/deserializers to handle fields containing these specific collections.
Complete sample code
Here is a complete example code covering all the key points mentioned above, showing how to use Jackson to do a deep copy of a DTO, ignoring the MultipartFile and its array:
import com.fasterxml.jackson.annotation.JsonIgnoreType; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.web.multipart.MultipartFile; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; /** * Demonstrate how to use Jackson to perform DTO deep copy and ignore specific
The above is the detailed content of Jackson DTO deep copy: non-invasively ignore specific types and their array fields. For more information, please follow other related articles on the PHP Chinese website!
Hot AI Tools
Undress AI Tool
Undress images for free
AI Clothes Remover
Online AI tool for removing clothes from photos.
Undresser.AI Undress
AI-powered app for creating realistic nude photos
ArtGPT
AI image generator for creative art from text prompts.
Stock Market GPT
AI powered investment research for smarter decisions
Hot Article
Popular tool
Notepad++7.3.1
Easy-to-use and free code editor
SublimeText3 Chinese version
Chinese version, very easy to use
Zend Studio 13.0.1
Powerful PHP integrated development environment
Dreamweaver CS6
Visual web development tools
SublimeText3 Mac version
God-level code editing software (SublimeText3)
Hot Topics
20518
7
13631
4
How to configure Spark distributed computing environment in Java_Java big data processing
Mar 09, 2026 pm 08:45 PM
Spark cannot run in local mode, ClassNotFoundException: org.apache.spark.sql.SparkSession. This is the most common first step of getting stuck: even the dependencies are not correct. Only spark-core_2.12 is written in Maven, but spark-sql_2.12 is not added. SparkSession crashes as soon as it is built. The Scala version must strictly match the official Spark compiled version - Spark3.4.x uses Scala2.12 by default. If you use spark-sqljar of 2.13, the class loader cannot directly find the main class. Practical advice: Go to mvnre
How to safely map user-entered weekday string to integer value and implement date offset operation in Java
Mar 09, 2026 pm 09:43 PM
This article introduces a concise and maintainable way to map the weekday string (such as "Monday") to the corresponding serial number (1-7), and use the modulo operation to realize the forward and backward offset of any number of days (such as Monday plus 4 days to get Friday), avoiding lengthy if chains and hard-coded logic.
How to generate a list of duplicate elements using Java's Collections.nCopies_Initialization tips
Mar 06, 2026 am 06:24 AM
Collections.nCopies returns an immutable view. Calling add/remove will throw UnsupportedOperationException; it needs to be wrapped with newArrayList() to modify it, and it is disabled for mutable objects.
How to use Homebrew to install Java on Mac_A must-have Java tool chain for developers
Mar 09, 2026 pm 09:48 PM
Homebrew installs the latest stable version of openjdk (such as JDK22) by default, not the LTS version; you need to explicitly execute brewinstallopenjdk@17 or brewinstallopenjdk@21 to install the LTS version, and manually configure PATH and JAVA_HOME to be correctly recognized by the system and IDE.
What is exception masking (Suppressed Exceptions) in Java_Multiple resource shutdown exception handling
Mar 10, 2026 pm 06:57 PM
What is SuppressedException: It is not "swallowed", but actively archived by the JVM. SuppressedException is not an exception loss, but the JVM quietly attaches the secondary exception to the main exception under the premise that "only one exception must be thrown" for you to verify afterwards. It is automatically triggered by the JVM in only two scenarios: one is that the resource closure in try-with-resources fails, and the other is that you manually call addSuppressed() in finally. The key difference is: the former is fully automatic and safe; the latter requires you to keep it to yourself, and it can be written as shadowing if you are not careful. try-
How to correctly implement runtime file writing in Java applications (avoiding JAR internal write failures)
Mar 09, 2026 pm 07:57 PM
After a Java application is packaged as a JAR, data cannot be written directly to the resources in the JAR package (such as test.txt) because the JAR is essentially a read-only ZIP archive; the correct approach is to write variable data to an external path (such as a user directory, a temporary directory, or a configuration-specified path).
What is the underlying principle of array expansion in Java_Java memory dynamic adjustment analysis
Mar 09, 2026 pm 09:45 PM
ArrayList.add() triggers expansion because grow() is called when size is equal to elementData.length. The first add allocates 10 capacity, and subsequent expansion is 1.5 times and not less than the minimum requirement, relying on delayed initialization and System.arraycopy optimization.
How to safely read a line of integer input in Java and avoid Scanner blocking
Mar 06, 2026 am 06:21 AM
This article introduces typical blocking problems when using Scanner to read multiple integers in a single line. It points out that hasNextInt() will wait indefinitely when there is no subsequent input, and recommends a safe alternative with nextLine() string splitting as the core.





