
在使用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输出中依然存在类似!或!的原生类型标签。这些标签在某些场景下可能是不必要的,甚至会影响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 vehicles; @JsonCreator public Vehicles(@JsonProperty("vehicles") final List vehicles) { this.vehicles = requireNonNull(vehicles); } public List 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:- ! name: "Dodge" type: "car"- ! name: "Scania" type: "truck"
从输出可以看出,JSON完美地移除了类型元数据,仅保留了name和type属性。然而,YAML输出中依然存在!和!这样的标签,这正是我们希望解决的问题。
解决方案:禁用YAMLGenerator.Feature.USE_NATIVE_TYPE_ID
Jackson jackson-dataformat-yaml库在处理多态类型时,默认会启用YAMLGenerator.Feature.USE_NATIVE_TYPE_ID特性。这个特性指示YAMLMapper在序列化时,利用YAML的原生标签(!符号后跟类型名)来标记对象的实际类型,即使已经通过@JsonTypeInfo指定了其他类型识别方式。这在某些场景下可能是有用的,例如,当没有显式的类型属性或需要更严格的类型声明时。
Remove.bg
AI在线抠图软件,图片去除背景
174 查看详情
要解决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"
可以看到,!和!标签已经被成功移除,YAML输出变得更加简洁,与JSON输出在类型元数据方面保持了一致。
注意事项与总结
特性作用: YAMLGenerator.Feature.USE_NATIVE_TYPE_ID是一个专门针对YAML格式的特性,它控制Jackson是否在序列化时使用YAML的原生类型标签来标记多态对象。默认情况下,此特性是启用的。适用场景: 如果你的应用中已经通过@JsonTypeInfo注解明确指定了类型识别方式(例如,通过一个现有属性type),并且不希望YAML输出中出现冗余的原生类型标签,那么禁用USE_NATIVE_TYPE_ID是正确的选择。潜在影响: 在某些复杂的多态场景下,或者当没有明确的类型属性来区分子类型时,原生类型标签可能提供了额外的类型信息,有助于反序列化。在禁用此特性之前,请确保你的类型识别机制(如@JsonTypeInfo)能够完全满足反序列化的需求。YAML版本: YAML 1.1规范中定义了隐式和显式标签。!属于显式标签的一种。禁用此特性意味着我们依赖Jackson在反序列化时通过其他方式(例如@JsonTypeInfo配置)来推断类型,而不是依赖YAML标签本身。
通过本文的教程,我们了解了Jackson在YAML序列化多态对象时出现原生类型标签的原因,并掌握了通过禁用YAMLGenerator.Feature.USE_NATIVE_TYPE_ID特性来解决这一问题的具体方法。这使得我们能够更好地控制YAML输出的格式,使其满足特定的简洁性和兼容性要求。
以上就是Jackson YAML序列化:如何移除多态对象的原生类型标签的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/960560.html
微信扫一扫
支付宝扫一扫