使用YamlDotNet根据操作类型动态调整YAML序列化输出属性名
现有场景与需求
目前使用YamlDotNet处理对象序列化时,希望根据操作类型调整输出的YAML结构。
原有类定义
State.cs
public class State { public string name { get; set; } public List<Operation> operations { get; set; } }
Operation.cs
public class Operation { public string name { get; set; } public string type { get; set; } }
当前实际序列化输出
states: - name: uPError operations: - name: switch on uP type: entry - name: switch off uP type: exit - name: test Do type: do
期望序列化输出
states: - name: uPError entryActions: [switch on uP] exitActions: [switch off uP] doActions: [test Do]
核心问题
已经可以拿到所有操作的类型与对应名称,需要把原本的operations列表,按照操作类型拆分对应为entryActions、exitActions、doActions三个独立属性,绑定对应类型的操作名称输出,实现目标格式的转换。
实现方案
方案一:调整类结构适配序列化规则(最简单,推荐)
YamlDotNet默认会将类的公开属性映射为YAML节点,只要把原有的operations属性标记为忽略序列化,再新增三个按类型筛选操作的计算属性,就能直接输出目标格式,不需要写额外序列化逻辑:
- 引入依赖命名空间
using YamlDotNet.Serialization; using System.Linq; - 改造State类定义
public class State { public string name { get; set; } // 标记原有operations属性不参与序列化,避免输出原始结构 [YamlIgnore] public List<Operation> operations { get; set; } // 新增三个分类属性,指定输出为内联Flow序列格式(即[xxx]样式) [YamlMember(SequenceStyle = SequenceStyle.Flow)] public List<string> entryActions => operations?.Where(op => op.type == "entry").Select(op => op.name).ToList() ?? new List<string>(); [YamlMember(SequenceStyle = SequenceStyle.Flow)] public List<string> exitActions => operations?.Where(op => op.type == "exit").Select(op => op.name).ToList() ?? new List<string>(); [YamlMember(SequenceStyle = SequenceStyle.Flow)] public List<string> doActions => operations?.Where(op => op.type == "do").Select(op => op.name).ToList() ?? new List<string>(); } - 构建默认序列化器直接使用即可
var serializer = new SerializerBuilder().Build(); var yaml = serializer.Serialize(stateList);
方案二:自定义类型转换器(不想修改原有类结构时使用)
如果保留原有State类的定义,不新增属性,就通过实现YamlDotNet的IYamlTypeConverter接口,自定义State类型的序列化逻辑:
- 编写自定义转换器
using YamlDotNet.Serialization; using YamlDotNet.Core; using YamlDotNet.Core.Events; using System.Linq; public class StateYamlConverter : IYamlTypeConverter { // 声明该转换器仅处理State类型 public bool Accepts(Type type) => type == typeof(State); // 仅需要序列化的话,反序列化方法留空即可 public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { throw new NotImplementedException(); } public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer rootSerializer) { var state = (State)value; // 开始映射节点 emitter.Emit(new MappingStart()); // 输出name字段 emitter.Emit(new Scalar("name")); emitter.Emit(new Scalar(state.name ?? string.Empty)); // 按类型筛选对应操作名称 var entryOps = state.operations?.Where(op => op.type == "entry").Select(op => op.name).ToList() ?? new List<string>(); var exitOps = state.operations?.Where(op => op.type == "exit").Select(op => op.name).ToList() ?? new List<string>(); var doOps = state.operations?.Where(op => op.type == "do").Select(op => op.name).ToList() ?? new List<string>(); // 调用公共方法输出内联序列 EmitInlineSequence(emitter, "entryActions", entryOps); EmitInlineSequence(emitter, "exitActions", exitOps); EmitInlineSequence(emitter, "doActions", doOps); emitter.Emit(new MappingEnd()); } // 抽出公共的内联序列输出方法,减少重复代码 private void EmitInlineSequence(IEmitter emitter, string key, List<string> items) { emitter.Emit(new Scalar(key)); emitter.Emit(new SequenceStart(null, null, false, SequenceStyle.Flow)); foreach (var item in items) { emitter.Emit(new Scalar(item)); } emitter.Emit(new SequenceEnd()); } } - 注册转换器到序列化配置中使用
var serializer = new SerializerBuilder() .WithTypeConverter(new StateYamlConverter()) .Build(); var yaml = serializer.Serialize(stateList);
注意事项
- 如果分类后的操作列表为空,不想输出对应的空键,可以在构建序列化器时开启
IgnorePropertiesWithDefaultValue选项,空列表会被自动跳过。 - 代码里的操作类型字符串(
entry/exit/do)建议抽成常量或枚举映射,避免硬编码拼写错误。 - 一定要指定序列为*Flow(内联)*风格,否则YamlDotNet默认会输出块式序列,和期望的
[xxx]内联格式不一致。
内容的提问来源于stack exchange,提问作者Priom Biswas
相关产品推荐
相关产品推荐

