You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SnakeYAML/Jackson将YAML转Java类时遇反序列化错误

解决Protobuf字段在Jackson/SnakeYAML反序列化YAML时的问题

我尝试用Jackson-dataformats-text(YAML)和SnakeYAML两种工具将指定YAML转换为Java类,MyClass中包含Protobuf生成的AbilityDto类型字段,但遇到了以下问题:

  • Jackson报错找不到ProtocolStringList的反序列化器
  • SnakeYAML报错AbilityDto无可写属性name

相关代码与配置

YAML内容

ability:
  name: my-ability-name!
  types:
    - source1
    - source2
  description: Description
  tags:
    - tag1
    - tag2
isActive: true

Jackson代码(原报错版本)

ObjectMapper mapper = new YAMLMapper();
MyClass xMyClass = mapper.readValue(yamlString, MyClass.class);

SnakeYAML代码(原报错版本)

Yaml yaml = new Yaml();
MyClass xMyClass = yaml.loadAs(yamlString, MyClass.class);

MyClass定义

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class MyClass {
    private Abilities.AbilityDto ability;
    private boolean isActive;
}

Protobuf定义

message AbilityDto {
  string id = 1;
  string name = 2;
  repeated string types = 3;
  optional string description = 4;
  repeated string tags = 5;
}

报错信息

Jackson报错
Cannot find a deserializer for non-concrete Collection type [collection type; class com.google.protobuf.ProtocolStringList, contains [simple type, class java.lang.String]]
 at [Source: (StringReader); line: 1, column: 1]
SnakeYAML报错
Cannot create property=rule for JavaBean=io.avalor.....MyClass@24f47796
 in 'string', line 1, column 1:
    ability:
    ^
Cannot create property=name for JavaBean=
 in 'string', line 2, column 3:
      name: "my-ability-name!"
      ^
No writable property 'name' on class: io.avalor.protocol...AbilityDto
 in 'string', line 2, column 9:
      name: "my-ability-name!"
            ^

 in 'string', line 2, column 3:
      name: "my-ability-name!"

解决方案

针对Jackson的问题

Jackson默认不支持Protobuf特有的类型(比如ProtocolStringList),需要引入Jackson的Protobuf扩展模块并注册到ObjectMapper中:

  1. 引入依赖(以Maven为例)
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-protobuf</artifactId>
    <version>你的Jackson版本号</version>
</dependency>
  1. 修改Jackson初始化代码
ObjectMapper mapper = new YAMLMapper()
    .registerModule(new ProtobufModule()); // 注册Protobuf模块
MyClass xMyClass = mapper.readValue(yamlString, MyClass.class);

该模块会自动处理Protobuf生成的所有类型,包括ProtocolStringList这类集合类型,解决反序列化转换器缺失的问题。

针对SnakeYAML的问题

Protobuf生成的Java类采用Builder模式,没有标准的JavaBean setter方法,SnakeYAML默认按JavaBean规则查找setter,因此找不到可写属性。可以通过以下两种方式解决:

方法1:直接访问字段(简单有效)

修改SnakeYAML的Constructor配置,让它直接访问类的字段,而不是依赖setter:

Constructor constructor = new Constructor(MyClass.class);
constructor.setBeanAccess(BeanAccess.FIELD); // 设置为字段访问模式
Yaml yaml = new Yaml(constructor);
MyClass xMyClass = yaml.loadAs(yamlString, MyClass.class);

方法2:自定义TypeConverter(灵活可控)

如果需要更精细的控制(比如处理默认值、字段映射),可以自定义TypeConverter来使用Protobuf的Builder构建对象:

TypeDescription abilityDtoDesc = TypeDescription.getTypeDescription(Abilities.AbilityDto.class);
TypeConverter abilityConverter = new TypeConverter() {
    @Override
    public Object convertTo(Class<?> type, Object value) {
        if (value instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) value;
            Abilities.AbilityDto.Builder builder = Abilities.AbilityDto.newBuilder();
            if (map.containsKey("name")) {
                builder.setName((String) map.get("name"));
            }
            if (map.containsKey("types")) {
                builder.addAllTypes((List<String>) map.get("types"));
            }
            // 其他字段同理处理
            return builder.build();
        }
        return null;
    }
};
Constructor constructor = new Constructor(MyClass.class);
constructor.addTypeConverter(abilityDtoDesc, abilityConverter);
Yaml yaml = new Yaml(constructor);
MyClass xMyClass = yaml.loadAs(yamlString, MyClass.class);

这种方式适合需要自定义字段映射逻辑的场景。


内容的提问来源于stack exchange,提问作者Liron Tal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 02:06:14