使用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中:
- 引入依赖(以Maven为例)
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-protobuf</artifactId> <version>你的Jackson版本号</version> </dependency>
- 修改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
相关产品推荐
相关产品推荐

