You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 23:07:05