0

0

利用Jackson忽略DTO中特定类型及其数组的非可序列化字段

心靈之曲

心靈之曲

发布时间:2025-10-13 09:49:38

|

393人浏览过

|

来源于php中文网

原创

利用jackson忽略dto中特定类型及其数组的非可序列化字段

本文探讨了在Spring Boot应用中,使用Jackson ObjectMapper 克隆包含非可序列化字段(如 MultipartFile)的DTOs时遇到的挑战。针对无法修改DTOs源文件的情况,我们介绍了如何通过 addMixIn 方法结合 @JsonIgnoreType 注解,有效忽略单个 MultipartFile 字段及其 MultipartFile[] 数组。文章详细阐述了针对数组类型需单独配置 addMixIn 的关键点,并提供了示例代码,帮助开发者在不修改DTO定义的前提下,实现安全的DTO深拷贝。

背景与挑战

在Spring Boot等Web应用开发中,DTO(Data Transfer Object)常用于数据传输。有时,我们需要对这些DTO进行深拷贝或克隆,例如在业务逻辑处理前保留原始数据,或在不同层之间传递独立的数据副本。一种常见的克隆策略是利用Java的序列化机制,如 ObjectOutputStream 和 ObjectInputStream。然而,当DTO中包含非 Serializable 字段(例如 org.springframework.web.multipart.MultipartFile,它通常用于文件上传)时,这种方法便不再适用,会抛出 NotSerializableException。

更具挑战性的是,我们可能无法控制这些DTO的源代码,这意味着我们不能直接添加 transient 关键字来忽略这些字段,也不能直接在字段上使用Jackson的 @JsonIgnore 注解。在这种限制下,我们需要一种外部配置的方式来告诉Jackson在序列化/反序列化过程中忽略特定类型的字段。

Jackson addMixIn 的初步应用

Jackson库提供了一种强大的机制——MixIn 注解,允许我们为已存在的类添加Jackson相关的注解,而无需修改原始类的源代码。这对于处理第三方库中的类或无法修改的DTOs非常有用。

为了忽略单个 MultipartFile 字段,我们可以定义一个空的 MixIn 接口或类,并用 @JsonIgnoreType 注解标记它,然后通过 ObjectMapper 的 addMixIn 方法将其应用到 MultipartFile.class:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonIgnoreType;
import org.springframework.web.multipart.MultipartFile;

public class DtoCloner {

    // 定义一个空的MixIn类,并用@JsonIgnoreType标记
    @JsonIgnoreType
    static class JacksonMixInForIgnoreType {}

    /**
     * 使用Jackson ObjectMapper克隆对象,忽略特定类型。
     * @param obj 待克隆的对象
     * @return 克隆后的对象
     * @throws RuntimeException 如果克隆过程中发生JSON处理异常
     */
    public Object makeClone(Object obj) {
        ObjectMapper mapper = new ObjectMapper();
        // 将MixIn应用到MultipartFile类,使其在序列化时被忽略
        mapper.addMixIn(MultipartFile.class, JacksonMixInForIgnoreType.class);

        try {
            // 先序列化为JSON字符串,再反序列化回对象
            String json = mapper.writeValueAsString(obj);
            return mapper.readValue(json, obj.getClass());
        } catch (JsonProcessingException e) {
            // 捕获并包装JSON处理异常
            throw new RuntimeException("Failed to clone object using Jackson", e);
        }
    }

    // 假设存在一个DTO类,其中包含MultipartFile字段
    public static class DeepCopyDto {
        private String name;
        private MultipartFile singleFile;
        // ... 其他字段

        public DeepCopyDto(String name, MultipartFile singleFile) {
            this.name = name;
            this.singleFile = singleFile;
        }

        // Getter/Setter省略
        public String getName() { return name; }
        public MultipartFile getSingleFile() { return singleFile; }
    }

    public static void main(String[] args) {
        // 示例用法
        // 注意:这里需要一个MultipartFile的实现,例如MockMultipartFile
        // MockMultipartFile mockFile = new MockMultipartFile("test", "hello.txt", "text/plain", "Hello World".getBytes());
        // DeepCopyDto originalDto = new DeepCopyDto("TestDto", mockFile);
        // DtoCloner cloner = new DtoCloner();
        // DeepCopyDto clonedDto = (DeepCopyDto) cloner.makeClone(originalDto);
        // System.out.println("Cloned DTO name: " + clonedDto.getName());
        // System.out.println("Cloned DTO singleFile: " + clonedDto.getSingleFile()); // 应该为null或被忽略
    }
}

上述代码对于DTO中包含 MultipartFile singleFile; 这样的单个字段是有效的。当Jackson尝试序列化 DeepCopyDto 时,会根据 addMixIn 的配置,直接忽略 singleFile 字段。

解决数组/集合类型字段的忽略问题

然而,当DTO中包含 MultipartFile[] fileArray; 这样的 MultipartFile 数组时,仅仅 mapper.addMixIn(MultipartFile.class, JacksonMixInForIgnoreType.class); 是不足以忽略整个数组的。Jackson在处理数组时,会尝试序列化数组的每个元素。如果数组元素类型本身是 MultipartFile,虽然单个 MultipartFile 被忽略了,但Jackson仍然会尝试处理数组结构本身,这可能导致在某些情况下,由于 MultipartFile 内部的非序列化属性(如 InputStream 的 FileDescriptor)而被触发序列化异常。

你好星识
你好星识

你的全能AI工作空间

下载

为了正确忽略 MultipartFile[] 类型的字段,我们需要明确地将 MixIn 应用到数组类型本身:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonIgnoreType;
import org.springframework.web.multipart.MultipartFile;

public class DtoClonerWithArraySupport {

    @JsonIgnoreType
    static class JacksonMixInForIgnoreType {}

    /**
     * 使用Jackson ObjectMapper克隆对象,忽略特定类型及其数组类型。
     * @param obj 待克隆的对象
     * @return 克隆后的对象
     * @throws RuntimeException 如果克隆过程中发生JSON处理异常
     */
    public Object makeClone(Object obj) {
        ObjectMapper mapper = new ObjectMapper();
        // 忽略单个MultipartFile类型
        mapper.addMixIn(MultipartFile.class, JacksonMixInForIgnoreType.class);
        // 关键:同时忽略MultipartFile数组类型
        mapper.addMixIn(MultipartFile[].class, JacksonMixInForIgnoreType.class);

        try {
            String json = mapper.writeValueAsString(obj);
            return mapper.readValue(json, obj.getClass());
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Failed to clone object using Jackson", e);
        }
    }

    // 假设存在一个DTO类,其中包含MultipartFile数组字段
    public static class DeepCopyDtoWithArray {
        private String name;
        private MultipartFile[] fileArray;

        public DeepCopyDtoWithArray(String name, MultipartFile[] fileArray) {
            this.name = name;
            this.fileArray = fileArray;
        }

        // Getter/Setter省略
        public String getName() { return name; }
        public MultipartFile[] getFileArray() { return fileArray; }
    }

    public static void main(String[] args) {
        // 示例用法 (需要MockMultipartFile)
        // MockMultipartFile mockFile1 = new MockMultipartFile("f1", "a.txt", "text/plain", "Content A".getBytes());
        // MockMultipartFile mockFile2 = new MockMultipartFile("f2", "b.txt", "text/plain", "Content B".getBytes());
        // MultipartFile[] files = {mockFile1, mockFile2};
        // DeepCopyDtoWithArray originalDto = new DeepCopyDtoWithArray("ArrayDto", files);
        // DtoClonerWithArraySupport cloner = new DtoClonerWithArraySupport();
        // DeepCopyDtoWithArray clonedDto = (DeepCopyDtoWithArray) cloner.makeClone(originalDto);
        // System.out.println("Cloned DTO name: " + clonedDto.getName());
        // System.out.println("Cloned DTO fileArray: " + clonedDto.getFileArray()); // 应该为null或被忽略
    }
}

通过同时配置 mapper.addMixIn(MultipartFile.class, JacksonMixInForIgnoreType.class); 和 mapper.addMixIn(MultipartFile[].class, JacksonMixInForIgnoreType.class);,我们能够确保无论是单个 MultipartFile 字段还是 MultipartFile 数组字段,都能够在Jackson的序列化过程中被有效忽略,从而避免 InvalidDefinitionException。

关于集合类型(如 List)的注意事项

值得注意的是,对于像 List 这样的泛型集合类型,直接使用 mapper.addMixIn(List.class, JacksonMixInForIgnoreType.class); 或 mapper.addMixIn(List.class, JacksonMixInForIgnoreType.class); 并不能达到忽略集合中特定类型元素的目的。Jackson处理泛型集合的方式与数组有所不同,它通常会序列化集合中的每个元素,而不是将集合本身作为一个整体来忽略。

在当前Jackson版本(如2.13.1)中,没有直接通过 addMixIn 机制来忽略特定泛型类型参数的集合的方法。如果需要忽略 List,可能需要更复杂的策略,例如:

  1. 自定义序列化器/反序列化器: 为 List 或包含该列表的DTO编写自定义的 JsonSerializer 和 JsonDeserializer,在其中手动跳过 MultipartFile 元素。
  2. @JsonView: 如果你的DTO结构允许,可以通过定义不同的视图来控制哪些字段在特定场景下被序列化。
  3. 使用 JsonFilter: 通过 ObjectMapper 注册 SimpleFilterProvider 和 PropertyFilter 来动态过滤属性。

然而,这些方法通常比 addMixIn 更复杂,且超出了本文主要讨论的范围。对于 MultipartFile 及其数组类型,上述 addMixIn 方案是最直接且有效的。

总结

当需要克隆包含非可序列化字段(特别是 MultipartFile 及其数组)的DTO,且无法修改DTO源代码时,Jackson的 addMixIn 机制提供了一个优雅的解决方案。关键在于,不仅要为基类型(如 MultipartFile.class)应用 MixIn,也要为对应的数组类型(如 MultipartFile[].class)进行相同的配置。这确保了Jackson在序列化和反序列化过程中能够正确识别并忽略这些非可序列化字段,从而实现DTO的深拷贝而避免运行时异常。对于泛型集合,addMixIn 的直接应用存在局限性,可能需要考虑其他Jackson高级特性或自定义实现。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

832

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

738

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

734

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16925

2023.08.03

公务员递补名单公布时间 公务员递补要求
公务员递补名单公布时间 公务员递补要求

公务员递补名单公布时间不固定,通常在面试前,由招录单位(如国家知识产权局、海关等)发布,依据是原入围考生放弃资格,会按笔试成绩从高到低递补,递补考生需按公告要求限时确认并提交材料,及时参加面试/体检等后续环节。要求核心是按招录单位公告及时响应、提交材料(确认书、资格复审材料)并准时参加面试。

0

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.7万人学习

Java 教程
Java 教程

共578课时 | 46.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号