
在使用jackson进行多态对象的yaml序列化时,`jackson-dataformat-yaml`默认会生成原生类型标签(如`!`),这与json序列化行为不同。本文将详细介绍如何通过禁用`yamlgenerator.feature.use_native_type_id`特性,从而在yaml输出中移除这些类型标签,实现更简洁的数据表示,并提供示例代码。
引言:Jackson YAML序列化中的多态类型标签问题
在使用Jackson库(包括jackson-databind和jackson-dataformat-yaml)处理多态对象的序列化时,开发者通常会利用@JsonTypeInfo和@JsonSubTypes注解来管理类型信息。例如,一个接口Vehicle有Car和Truck两个实现类,我们希望在序列化时,通过一个特定的属性(如type)来标识具体的子类型,而不是依赖于Java类的完全限定名。
然而,在实际操作中,我们可能会发现一个不一致的行为:当序列化到JSON格式时,输出结果通常是干净的,只包含业务数据和我们指定的类型属性;但当序列化到YAML格式时,除了业务数据和类型属性外,jackson-dataformat-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 com.google.common.collect.ImmutableList;import lombok.Value;import java.util.List;import static java.util.Objects.requireNonNull;public class YamlTypeTagRemoval { @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")}) public interface Vehicle { String getName(); } @Value public static class Car implements Vehicle { String name; String type = "car"; @JsonCreator public Car(@JsonProperty("name") final String name) { this.name = requireNonNull(name); } } @Value public static class Truck implements Vehicle { String name; String type = "truck"; @JsonCreator public Truck(@JsonProperty("name") final String name) { this.name = requireNonNull(name); } } @Value public static class Vehicles { List vehicles; @JsonCreator public Vehicles(@JsonProperty("vehicles") final List vehicles) { super(); this.vehicles = requireNonNull(vehicles); } } public static void main(String[] args) throws JsonProcessingException { ObjectMapper MAPPER = new ObjectMapper(); ObjectMapper YAML_MAPPER = YAMLMapper.builder() .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) .build(); final Vehicles vehicles = new Vehicles(ImmutableList.of(new Car("Dodge"), new Truck("Scania"))); final String json = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(vehicles); System.out.println("JSON Output:n" + json); final String yaml = YAML_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(vehicles); System.out.println("nYAML Output (with tags):n" + yaml); }}
运行上述代码,我们将得到以下输出:
JSON Output:{ "vehicles" : [ { "name" : "Dodge", "type" : "car" }, { "name" : "Scania", "type" : "truck" } ]}YAML Output (with tags):vehicles:- ! name: "Dodge" type: "car"- ! name: "Scania" type: "truck"
可以看到,JSON输出完全符合预期,没有额外的类型元数据。然而,YAML输出中却出现了!和!这样的标签,这正是我们需要解决的问题。
理解原生类型ID标签
在YAML规范中,!符号后跟一个名称(如!tag)用于表示一个特定的类型标签。jackson-dataformat-yaml库在处理多态对象时,默认会启用一个特性来生成这些“原生类型ID”标签,以便在反序列化时能够准确地识别对象的具体类型。这个特性就是YAMLGenerator.Feature.USE_NATIVE_TYPE_ID。
USE_NATIVE_TYPE_ID特性默认是启用的,它的作用是让YAMLGenerator在序列化多态类型时,尝试使用YAML的原生标签机制来表示对象的类型。当Jackson检测到需要包含类型信息,并且@JsonTypeInfo配置允许(例如,当include属性不是JsonTypeInfo.As.EXISTING_PROPERTY,或者即使是EXISTING_PROPERTY,但jackson-dataformat-yaml仍然决定添加原生标签以提供更强的类型提示时),它就会生成这些标签。在我们的示例中,尽管我们使用了JsonTypeInfo.As.EXISTING_PROPERTY并通过type属性来指示类型,jackson-dataformat-yaml仍然默认添加了原生标签。
解决方案:禁用 USE_NATIVE_TYPE_ID 特性
要移除YAML输出中的原生类型标签,最直接的方法就是在构建YAMLMapper时,显式地禁用YAMLGenerator.Feature.USE_NATIVE_TYPE_ID特性。
博思AIPPT
博思AIPPT来了,海量PPT模板任选,零基础也能快速用AI制作PPT。
117 查看详情
修改YAML_MAPPER的构建代码如下:
ObjectMapper YAML_MAPPER = YAMLMapper.builder() .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) .disable(YAMLGenerator.Feature.USE_NATIVE_TYPE_ID) // 禁用原生类型ID标签 .build();
通过添加.disable(YAMLGenerator.Feature.USE_NATIVE_TYPE_ID)这一行,我们明确告诉Jackson在序列化YAML时不要生成这些原生类型标签。
完整示例与验证
将上述修改整合到main方法中,完整的示例代码如下:
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 com.google.common.collect.ImmutableList;import lombok.Value;import java.util.List;import static java.util.Objects.requireNonNull;public class YamlTypeTagRemovalFixed { @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")}) public interface Vehicle { String getName(); } @Value public static class Car implements Vehicle { String name; String type = "car"; @JsonCreator public Car(@JsonProperty("name") final String name) { this.name = requireNonNull(name); } } @Value public static class Truck implements Vehicle { String name; String type = "truck"; @JsonCreator public Truck(@JsonProperty("name") final String name) { this.name = requireNonNull(name); } } @Value public static class Vehicles { List vehicles; @JsonCreator public Vehicles(@JsonProperty("vehicles") final List vehicles) { super(); this.vehicles = requireNonNull(vehicles); } } public static void main(String[] args) throws JsonProcessingException { ObjectMapper MAPPER = new ObjectMapper(); // 禁用 YAMLGenerator.Feature.USE_NATIVE_TYPE_ID 来移除原生类型标签 ObjectMapper YAML_MAPPER = YAMLMapper.builder() .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) .disable(YAMLGenerator.Feature.USE_NATIVE_TYPE_ID) .build(); final Vehicles vehicles = new Vehicles(ImmutableList.of(new Car("Dodge"), new Truck("Scania"))); final String json = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(vehicles); System.out.println("JSON Output:n" + json); final String yaml = YAML_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(vehicles); System.out.println("nYAML Output (without tags):n" + yaml); }}
再次运行代码,我们将得到以下输出:
JSON Output:{ "vehicles" : [ { "name" : "Dodge", "type" : "car" }, { "name" : "Scania", "type" : "truck" } ]}YAML Output (without tags):vehicles:- name: "Dodge" type: "car"- name: "Scania" type: "truck"
现在,YAML输出已经移除了!和!等原生类型标签,实现了与JSON输出一致的简洁性。
注意事项
反序列化的影响:禁用USE_NATIVE_TYPE_ID后,如果您的反序列化过程依赖于这些原生YAML标签来识别类型,那么您需要确保通过其他机制(例如,本例中@JsonTypeInfo结合property = “type”的方式)提供了足够的类型信息。在本例中,由于我们已经通过type属性明确指定了子类型,因此禁用原生标签并不会影响正常的反序列化功能。WRITE_DOC_START_MARKER:示例代码中还禁用了YAMLGenerator.Feature.WRITE_DOC_START_MARKER。这个特性控制是否在YAML文档开头写入—标记。它与类型标签无关,但也是jackson-dataformat-yaml中常用的一个配置选项,用于控制YAML文档的格式。Jackson版本:确保您使用的Jackson库版本(包括jackson-databind和jackson-dataformat-yaml)是相对较新的稳定版本,以避免潜在的兼容性问题或未预期的行为。
总结
jackson-dataformat-yaml库在处理多态对象序列化时,默认会启用YAMLGenerator.Feature.USE_NATIVE_TYPE_ID特性,从而在YAML输出中生成原生类型标签。虽然这在某些场景下提供了一种强类型提示,但在追求简洁和与JSON输出一致性的情况下,这些标签可能是多余的。通过在构建YAMLMapper时显式禁用YAMLGenerator.Feature.USE_NATIVE_TYPE_ID,我们可以轻松地移除这些标签,从而获得更符合预期的YAML输出。在进行此项配置时,请务必考虑其对反序列化过程可能产生的影响,并确保您的类型解析策略仍然有效。
以上就是Jackson YAML序列化:禁用多态对象原生类型标签的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/960440.html
微信扫一扫
支付宝扫一扫