Java中SnakeYAML如何自定义控制YAML流样式?
自定义SnakeYAML节点的序列化样式(混合BLOCK和FLOW)
你遇到的问题确实是SnakeYAML全局FlowStyle设置的局限——没法同时让不同节点用不同的样式。不过好在我们可以通过自定义Representer来实现这个需求,不用拆分导出,也能完美保留锚点和别名的功能。
下面分享两种可行的解决方案:
方案一:针对特定键自定义样式
如果已经明确知道哪些节点需要特殊样式(比如你的Parent-2),可以直接在自定义Representer里硬编码判断条件,给目标节点单独设置FLOW样式,其他节点沿用全局BLOCK样式。
import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.Tag; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class CustomYamlStyleDemo { public static void main(String[] args) { Map<String, Integer> children1 = new LinkedHashMap<>(); children1.put("Criteria-1", 2); children1.put("Criteria-2", 1); List<List<Object>> children2 = new ArrayList<>(); List<Object> list = new ArrayList<>(); list.add("Criteria-1"); list.add("Criteria-2"); list.add(1); children2.add(list); Map<String, Object> map = new LinkedHashMap<>(); map.put("Version", 2.0); map.put("Parent-1", children1); map.put("Parent-2", children2); // 自定义Representer,单独处理指定节点的样式 Representer customRepresenter = new Representer() { @Override protected Node representMapping(Tag tag, Map<?, ?> mapping, boolean flowStyle) { // 全局默认用BLOCK样式序列化Map Node mapNode = super.representMapping(tag, mapping, DumperOptions.FlowStyle.BLOCK.getBoolean()); // 遍历Map条目,找到Parent-2对应的子节点 for (Map.Entry<?, ?> entry : mapping.entrySet()) { if ("Parent-2".equals(entry.getKey())) { Node targetNode = representData(entry.getValue()); // 给该节点设置FLOW样式 targetNode.setFlowStyle(DumperOptions.FlowStyle.FLOW); // 更新Map节点中的对应子节点 int keyIndex = mapNode.getValue().indexOf(representData(entry.getKey())); mapNode.getValue().set(keyIndex + 1, targetNode); } } return mapNode; } }; DumperOptions options = new DumperOptions(); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); Yaml yaml = new Yaml(customRepresenter, options); System.out.println(yaml.dump(map)); } }
运行后输出完全符合你的要求:
Version: 2.0 Parent-1: Criteria-1: 2 Criteria-2: 1 Parent-2: - [Criteria-1, Criteria-2, 1]
方案二:用包装类标记需要FLOW样式的节点
如果你的场景更灵活,不想硬编码键名,可以创建一个包装类,用来标记任何需要用FLOW样式序列化的对象。这种方式扩展性更强,适合需要动态指定样式的场景。
import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.nodes.Node; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class WrapperBasedYamlStyleDemo { // 自定义包装类,标记需要用FLOW样式的对象 static class FlowStyleWrapper<T> { private final T value; public FlowStyleWrapper(T value) { this.value = value; } public T getValue() { return value; } } public static void main(String[] args) { Map<String, Integer> children1 = new LinkedHashMap<>(); children1.put("Criteria-1", 2); children1.put("Criteria-2", 1); List<List<Object>> children2 = new ArrayList<>(); List<Object> list = new ArrayList<>(); list.add("Criteria-1"); list.add("Criteria-2"); list.add(1); children2.add(list); Map<String, Object> map = new LinkedHashMap<>(); map.put("Version", 2.0); map.put("Parent-1", children1); // 用包装类把需要FLOW样式的对象包裹起来 map.put("Parent-2", new FlowStyleWrapper<>(children2)); // 自定义Representer识别包装类 Representer customRepresenter = new Representer() { @Override protected Node representData(Object data) { if (data instanceof FlowStyleWrapper) { // 序列化包装类内的实际对象,并设置为FLOW样式 Node node = super.representData(((FlowStyleWrapper<?>) data).getValue()); node.setFlowStyle(DumperOptions.FlowStyle.FLOW); return node; } // 其他对象沿用全局BLOCK样式 return super.representData(data); } }; DumperOptions options = new DumperOptions(); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); Yaml yaml = new Yaml(customRepresenter, options); System.out.println(yaml.dump(map)); } }
关于锚点和别名的说明
这两种方案都不会破坏SnakeYAML的锚点/别名功能——我们只是修改了节点的序列化样式,没有改变SnakeYAML处理对象引用的底层逻辑。你可以正常使用锚点相关的API,生成的YAML会正确保留锚点和别名。
内容的提问来源于stack exchange,提问作者Sarah N
相关产品推荐
相关产品推荐

