如何构建自定义SnakeYAML Constructor实现YAML文件的模块化反序列化
解决方案
你遇到的问题根源是DocumentRoot中actions字段定义为List<Map<String, Object>>,SnakeYAML默认会将Object类型的映射值反序列化为LinkedHashMap,你添加的TypeDescription未生效是因为没有指定触发该类型反序列化的场景。
直接自定义Constructor重写节点构造逻辑即可实现需求,修改后的createYaml方法如下:
private static Yaml createYaml(List<ActionDescriptor<?>> descriptors) { // 提前构建actionKey到对应值类型的映射 Map<String, Class<?>> actionTypeMapping = descriptors.stream() .collect(Collectors.toMap(ActionDescriptor::actionKey, ActionDescriptor::actionValueType)); // 自定义Constructor处理actions列表的反序列化 Constructor constructor = new Constructor(DocumentRoot.class) { @Override protected Object constructObject(Node node) { // 匹配actions列表中的单个action映射节点 if (node instanceof MappingNode mappingNode && node.getParentNode() instanceof SequenceNode parentSeq && parentSeq.getParentNode() != null && "actions".equals(parentSeq.getParentNode().getPropertyName())) { // 每个action都是单键映射,取唯一键值对 if (mappingNode.getValue().size() == 1) { NodeTuple tuple = mappingNode.getValue().get(0); String actionKey = ((ScalarNode) tuple.getKeyNode()).getValue(); Class<?> targetType = actionTypeMapping.get(actionKey); if (targetType != null) { // 手动设置值节点的目标类型,让SnakeYAML按指定类型反序列化 tuple.getValueNode().setType(targetType); } } } return super.constructObject(node); } }; // 注册所有动作值类型的描述,保证字段反序列化正常 for (ActionDescriptor<?> descriptor : descriptors) { constructor.addTypeDescription(new TypeDescription(descriptor.actionValueType())); } Yaml yaml = new Yaml(constructor); yaml.setBeanAccess(BeanAccess.FIELD); return yaml; }
实现逻辑说明
- 提前构建动作key到对应值类型的映射表,方便快速查询
- 重写
constructObject方法,在反序列化每个节点时做判断:如果当前节点是actions列表下的单键映射节点,就取出映射的key,从映射表中查到对应的目标类型 - 手动给值节点设置目标类型,SnakeYAML后续就会按照指定类型反序列化值,而非默认转为
LinkedHashMap - 保留原有
TypeDescription注册逻辑,保证自定义值类型的字段能正常反序列化
修改后运行即可得到你期望的输出,不需要修改YAML文件添加额外标签。
内容的提问来源于stack exchange,提问作者Stefan Dollase
相关产品推荐
相关产品推荐

