0

0

Jackson YAML序列化:如何移除多态对象的原生类型标签

霞舞

霞舞

发布时间:2025-11-27 17:01:04

|

850人浏览过

|

来源于php中文网

原创

Jackson YAML序列化:如何移除多态对象的原生类型标签

在使用jackson进行多态对象的yaml序列化时,即使已配置`@jsontypeinfo`通过现有属性识别类型,yaml输出仍可能默认包含如`!`这样的原生类型标签。本文将深入探讨这一现象的成因,并提供通过禁用`yamlgenerator.feature.use_native_type_id`特性来移除这些标签的专业教程,确保yaml输出与json保持一致的简洁性。

引言:Jackson多态序列化与YAML类型标签问题

Jackson库在Java应用中广泛用于JSON和YAML数据的序列化与反序列化。对于包含多态类型的对象,例如一个接口Vehicle及其实现类Car和Truck,我们通常会使用@JsonTypeInfo注解来指导Jackson如何识别和处理这些子类型。当配置@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")时,我们期望Jackson通过对象中已存在的type属性来区分不同的子类型,并在序列化时避免额外添加类型元数据。

对于JSON序列化,这种配置通常能达到预期效果,输出的JSON中不会包含任何额外的类元信息。然而,在使用jackson-dataformat-yaml进行YAML序列化时,即使采用了相同的多态配置,我们可能会发现YAML输出中依然存在类似!<car>或!<truck>的原生类型标签。这些标签在某些场景下可能是不必要的,甚至会影响YAML的可读性和与其他系统的兼容性。

问题复现示例

为了更清晰地说明这一问题,我们来看一个具体的代码示例。假设我们有Vehicle接口和Car、Truck两个实现类,以及一个包含Vehicle列表的Vehicles容器类。

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import java.util.Arrays;
import java.util.List;
import static java.util.Objects.requireNonNull;

// 定义多态接口
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.EXISTING_PROPERTY,
    property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = Car.class, name = "car"),
    @JsonSubTypes.Type(value = Truck.class, name = "truck") })
interface Vehicle {
    String getName();
}

// Car 实现类
class Car implements Vehicle {
    String name;
    String type = "car"; // 类型标识属性

    @JsonCreator
    public Car(@JsonProperty("name") final String name) {
        this.name = requireNonNull(name);
    }

    @Override
    public String getName() {
        return name;
    }

    public String getType() {
        return type;
    }
}

// Truck 实现类
class Truck implements Vehicle {
    String name;
    String type = "truck"; // 类型标识属性

    @JsonCreator
    public Truck(@JsonProperty("name") final String name) {
        this.name = requireNonNull(name);
    }

    @Override
    public String getName() {
        return name;
    }

    public String getType() {
        return type;
    }
}

// 包含Vehicle列表的容器类
class Vehicles {
    List<Vehicle> vehicles;

    @JsonCreator
    public Vehicles(@JsonProperty("vehicles") final List<Vehicle> vehicles) {
        this.vehicles = requireNonNull(vehicles);
    }

    public List<Vehicle> getVehicles() {
        return vehicles;
    }
}

public class YamlTagRemovalDemo {
    public static void main(String[] args) throws JsonProcessingException {
        // 标准JSON ObjectMapper
        ObjectMapper JSON_MAPPER = new ObjectMapper();

        // YAML ObjectMapper,默认配置
        ObjectMapper YAML_MAPPER_DEFAULT = YAMLMapper.builder()
            .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) // 禁用文档开始标记
            .build();

        final Vehicles vehicles = new Vehicles(Arrays.asList(new Car("Dodge"), new Truck("Scania")));

        System.out.println("--- JSON Output ---");
        final String json = JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(vehicles);
        System.out.println(json);

        System.out.println("\n--- YAML Output (Default) ---");
        final String yamlDefault = YAML_MAPPER_DEFAULT.writerWithDefaultPrettyPrinter().writeValueAsString(vehicles);
        System.out.println(yamlDefault);
    }
}

运行上述代码,我们将得到如下输出:

JSON Output:

{
  "vehicles" : [ {
    "name" : "Dodge",
    "type" : "car"
  }, {
    "name" : "Scania",
    "type" : "truck"
  } ]
}

YAML Output (Default):

vehicles:
- !<car>
  name: "Dodge"
  type: "car"
- !<truck>
  name: "Scania"
  type: "truck"

从输出可以看出,JSON完美地移除了类型元数据,仅保留了name和type属性。然而,YAML输出中依然存在!<car>和!<truck>这样的标签,这正是我们希望解决的问题。

解决方案:禁用YAMLGenerator.Feature.USE_NATIVE_TYPE_ID

Jackson jackson-dataformat-yaml库在处理多态类型时,默认会启用YAMLGenerator.Feature.USE_NATIVE_TYPE_ID特性。这个特性指示YAMLMapper在序列化时,利用YAML的原生标签(!符号后跟类型名)来标记对象的实际类型,即使已经通过@JsonTypeInfo指定了其他类型识别方式。这在某些场景下可能是有用的,例如,当没有显式的类型属性或需要更严格的类型声明时。

PathFinder
PathFinder

AI驱动的销售漏斗分析工具

下载

要解决YAML输出中多余类型标签的问题,我们只需在构建YAMLMapper时禁用此特性即可。

修改main方法中的YAML_MAPPER构建部分:

public class YamlTagRemovalDemo {
    public static void main(String[] args) throws JsonProcessingException {
        // ... (其他代码不变)

        // YAML ObjectMapper,禁用原生类型ID特性
        ObjectMapper YAML_MAPPER_FIXED = YAMLMapper.builder()
            .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) // 禁用文档开始标记
            .disable(YAMLGenerator.Feature.USE_NATIVE_TYPE_ID) // 禁用原生类型ID
            .build();

        final Vehicles vehicles = new Vehicles(Arrays.asList(new Car("Dodge"), new Truck("Scania")));

        // ... (JSON 输出不变)

        System.out.println("\n--- YAML Output (Fixed) ---");
        final String yamlFixed = YAML_MAPPER_FIXED.writerWithDefaultPrettyPrinter().writeValueAsString(vehicles);
        System.out.println(yamlFixed);
    }
}

重新运行代码,现在“YAML Output (Fixed)”部分将产生我们期望的输出:

YAML Output (Fixed):

vehicles:
- name: "Dodge"
  type: "car"
- name: "Scania"
  type: "truck"

可以看到,!<car>和!<truck>标签已经被成功移除,YAML输出变得更加简洁,与JSON输出在类型元数据方面保持了一致。

注意事项与总结

  1. 特性作用: YAMLGenerator.Feature.USE_NATIVE_TYPE_ID是一个专门针对YAML格式的特性,它控制Jackson是否在序列化时使用YAML的原生类型标签来标记多态对象。默认情况下,此特性是启用的。
  2. 适用场景: 如果你的应用中已经通过@JsonTypeInfo注解明确指定了类型识别方式(例如,通过一个现有属性type),并且不希望YAML输出中出现冗余的原生类型标签,那么禁用USE_NATIVE_TYPE_ID是正确的选择。
  3. 潜在影响: 在某些复杂的多态场景下,或者当没有明确的类型属性来区分子类型时,原生类型标签可能提供了额外的类型信息,有助于反序列化。在禁用此特性之前,请确保你的类型识别机制(如@JsonTypeInfo)能够完全满足反序列化的需求。
  4. YAML版本: YAML 1.1规范中定义了隐式和显式标签。!<type>属于显式标签的一种。禁用此特性意味着我们依赖Jackson在反序列化时通过其他方式(例如@JsonTypeInfo配置)来推断类型,而不是依赖YAML标签本身。

通过本文的教程,我们了解了Jackson在YAML序列化多态对象时出现原生类型标签的原因,并掌握了通过禁用YAMLGenerator.Feature.USE_NATIVE_TYPE_ID特性来解决这一问题的具体方法。这使得我们能够更好地控制YAML输出的格式,使其满足特定的简洁性和兼容性要求。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

457

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

549

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

337

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

82

2025.09.10

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

27

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

27

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1946

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

656

2025.10.17

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

37

2026.03.12

热门下载

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

精品课程

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

共23课时 | 4.4万人学习

C# 教程
C# 教程

共94课时 | 11.2万人学习

Java 教程
Java 教程

共578课时 | 81.2万人学习

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

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