如何用YamlDotNet反序列化未知成员?求正确配置方法
YamlDotNet反序列化未知属性报错及解决方案
报错信息
YamlDotNet.Core.YamlException: 'Property 'unknown1' not found.
原代码
using System.Runtime.Serialization; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; var input = @" Name: John Count: 34 unknown1: xyz: value1 abc: value2 unknown2: foo "; var serializer = new SerializerBuilder() .WithNamingConvention(PascalCaseNamingConvention.Instance) .Build(); var deserializer = new DeserializerBuilder() .WithNamingConvention(PascalCaseNamingConvention.Instance) .Build(); public class Person { public string Name { get; set; } public string Count { get; set; } // 用于记录YAML中所有未知成员,序列化时需与Name、Count同级输出 public Dictionary<string, string> UnknownProperties { get; set; } }
需求
将YAML中未定义的属性(如unknown1、unknown2,结构未知)存入Person类的UnknownProperties字典,支持两种格式:
Dictionary<string, string>:值保留原始YAML字符串,例如{"unknown1", "xyz: value1\nabc: value2"} {"unknown2", "foo"}Dictionary<string, object>:值解析为对应类型,例如{"unknown1", {{"xyz","value1"},{"abc", "value2"}}} {"unknown2", "foo"}
解决方案
方案1:存储解析后的未知属性(Dictionary<string, object>)
利用YamlDotNet的WithUnmatchedProperties()方法,直接指定将未知属性存入目标字典,实现最简单的需求:
using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; using System.Collections.Generic; var input = @" Name: John Count: 34 unknown1: xyz: value1 abc: value2 unknown2: foo "; // 配置反序列化器,指定未知属性存入UnknownProperties var deserializer = new DeserializerBuilder() .WithNamingConvention(PascalCaseNamingConvention.Instance) .WithUnmatchedProperties(p => p.UnknownProperties) .Build(); Person person = deserializer.Deserialize<Person>(input); // 配置序列化器,确保未知属性与已知属性同级输出 var serializer = new SerializerBuilder() .WithNamingConvention(PascalCaseNamingConvention.Instance) .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) .Build(); string outputYaml = serializer.Serialize(person); Console.WriteLine(outputYaml); public class Person { public string Name { get; set; } public string Count { get; set; } // 初始化字典避免空引用 public Dictionary<string, object> UnknownProperties { get; set; } = new Dictionary<string, object>(); }
效果:
unknown1会被解析为Dictionary<string, object>类型,unknown2为string类型,均存入UnknownProperties- 序列化时,未知属性会与
Name、Count同级输出,保持原YAML结构
方案2:存储原始YAML字符串(Dictionary<string, string>)
若需要保留未知属性的原始YAML格式,需自定义IUnmappedPropertyVisitor捕获节点内容,同时自定义转换器实现正确序列化:
using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; using System.Collections.Generic; using System.Text; using System.IO; var input = @" Name: John Count: 34 unknown1: xyz: value1 abc: value2 unknown2: foo "; var deserializer = new DeserializerBuilder() .WithNamingConvention(PascalCaseNamingConvention.Instance) .WithUnmappedPropertyVisitor(new UnknownPropertyCollector()) .Build(); Person person = deserializer.Deserialize<Person>(input); var serializer = new SerializerBuilder() .WithNamingConvention(PascalCaseNamingConvention.Instance) .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) .WithTypeConverter(new UnknownPropertiesConverter()) .Build(); string outputYaml = serializer.Serialize(person); Console.WriteLine(outputYaml); public class Person { public string Name { get; set; } public string Count { get; set; } public Dictionary<string, string> UnknownProperties { get; set; } = new Dictionary<string, string>(); } // 捕获未知属性的原始YAML值 public class UnknownPropertyCollector : IUnmappedPropertyVisitor { public void Visit(IObjectDescriptor target, KeyValuePair<Scalar, NodeEvent> unmappedProperty) { if (target.Value is Person person) { string key = unmappedProperty.Key.Value; NodeEvent valueNode = unmappedProperty.Value; // 生成值节点的原始YAML字符串 var yamlBuilder = new StringBuilder(); var emitter = new Emitter(new StringWriter(yamlBuilder)); valueNode.Emit(emitter); person.UnknownProperties[key] = yamlBuilder.ToString().Trim(); } } } // 自定义转换器,实现未知属性的同级序列化 public class UnknownPropertiesConverter : IYamlTypeConverter { public bool Accepts(Type type) => type == typeof(Person); public object ReadYaml(IParser parser, Type type) { // 复用反序列化逻辑 var innerDeserializer = new DeserializerBuilder() .WithNamingConvention(PascalCaseNamingConvention.Instance) .WithUnmappedPropertyVisitor(new UnknownPropertyCollector()) .Build(); return innerDeserializer.Deserialize<Person>(parser); } public void WriteYaml(IEmitter emitter, object value, Type type) { if (value is Person person) { // 序列化已知属性 var knownData = new { person.Name, person.Count }; var knownSerializer = new SerializerBuilder() .WithNamingConvention(PascalCaseNamingConvention.Instance) .Build(); knownSerializer.Serialize(emitter, knownData); // 序列化未知属性 foreach (var kvp in person.UnknownProperties) { emitter.Emit(new Scalar(kvp.Key)); // 将原始字符串解析为YAML节点后输出,保证格式正确 var nodeDeserializer = new DeserializerBuilder().Build(); var yamlNode = nodeDeserializer.Deserialize<YamlDotNet.RepresentationModel.YamlNode>(new StringReader(kvp.Value)); yamlNode.Emit(emitter); } } } }
效果:
UnknownProperties中存储每个未知属性值的原始YAML字符串,例如unknown1对应"xyz: value1\nabc: value2"- 序列化时,未知属性与已知属性同级输出,保持原格式
内容的提问来源于stack exchange,提问作者Kux
相关产品推荐
相关产品推荐

