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

使用YamlDotNet反序列化时,如何防止重复设置属性并触发报错?

如何让YamlDotNet在遇到重复属性时抛出异常

我完全懂你的困扰——配置文件里出现重复属性本来就不符合预期,YamlDotNet默认的覆盖行为确实会导致难以排查的问题。要实现第二次设置同一属性时抛出异常,我们可以通过自定义YamlDotNet的属性处理逻辑来实现,下面是具体的方案:

实现思路

核心思路是跟踪每个对象的属性设置状态,当尝试设置一个已经被赋值过的属性时,直接抛出InvalidOperationException。我们可以通过自定义PropertyDescriptor和对应的工厂类,替换YamlDotNet默认的属性处理逻辑来实现。

具体步骤

1. 创建属性跟踪工具类

首先我们需要一个工具类来安全地跟踪每个对象的已设置属性,避免内存泄漏:

using System.Collections.Generic;
using System.Runtime.CompilerServices;

public static class PropertyTracking
{
    private static readonly ConditionalWeakTable<object, HashSet<string>> _trackedProperties = new();

    public static bool IsPropertySet(object obj, string propertyName)
    {
        return _trackedProperties.TryGetValue(obj, out var properties) && properties.Contains(propertyName);
    }

    public static void MarkPropertyAsSet(object obj, string propertyName)
    {
        if (!_trackedProperties.TryGetValue(obj, out var properties))
        {
            properties = new HashSet<string>();
            _trackedProperties.Add(obj, properties);
        }
        properties.Add(propertyName);
    }
}

2. 自定义抛出异常的PropertyDescriptor

接下来创建一个自定义的PropertyDescriptor,继承自YamlDotNet的ReflectionPropertyDescriptor,重写SetValue方法来添加重复检查:

using System.Reflection;
using System;
using YamlDotNet.Serialization;

public class ThrowingOnDuplicatePropertyDescriptor : ReflectionPropertyDescriptor
{
    public ThrowingOnDuplicatePropertyDescriptor(IPropertyDescriptor baseDescriptor)
        : base(
            baseDescriptor.PropertyInfo,
            baseDescriptor.Type,
            baseDescriptor.Name,
            baseDescriptor.CanWrite,
            baseDescriptor.CanRead,
            baseDescriptor.SerializationOrder,
            baseDescriptor.ScalarStyle,
            baseDescriptor.DefaultValue,
            baseDescriptor.Alias,
            baseDescriptor.ApplyNamingConvention
        )
    {
    }

    public override void SetValue(object target, object value)
    {
        if (PropertyTracking.IsPropertySet(target, Name))
        {
            throw new InvalidOperationException($"Property '{Name}' has already been set on object of type '{target.GetType().FullName}'. Duplicate properties are not allowed.");
        }

        base.SetValue(target, value);
        PropertyTracking.MarkPropertyAsSet(target, Name);
    }
}

3. 自定义PropertyDescriptor工厂类

然后创建一个工厂类,用来生成我们自定义的PropertyDescriptor:

using System.Reflection;
using YamlDotNet.Serialization;

public class ThrowingOnDuplicatePropertyDescriptorFactory : IPropertyDescriptorFactory
{
    private readonly IPropertyDescriptorFactory _innerFactory;

    public ThrowingOnDuplicatePropertyDescriptorFactory(IPropertyDescriptorFactory innerFactory)
    {
        _innerFactory = innerFactory;
    }

    public IPropertyDescriptor Create(PropertyInfo propertyInfo)
    {
        var baseDescriptor = _innerFactory.Create(propertyInfo);
        return new ThrowingOnDuplicatePropertyDescriptor(baseDescriptor);
    }
}

4. 配置Deserializer使用自定义逻辑

最后,在构建Deserializer的时候,替换默认的IPropertyDescriptorFactory:

var deserializer = new DeserializerBuilder()
    .WithPropertyDescriptorFactory(inner => new ThrowingOnDuplicatePropertyDescriptorFactory(inner))
    .Build();

// 测试反序列化你的YAML片段
var yaml = @"
bill-to: &id001
  street: |-
    123 Tornado Alley
    Suite 16
  city: East Westville
  city: West Westville
  state: KS
";

// 对应的实体类示例
public class Config
{
    public BillTo BillTo { get; set; }
}

public class BillTo
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

try
{
    var result = deserializer.Deserialize<Config>(yaml);
}
catch (InvalidOperationException ex)
{
    Console.WriteLine(ex.Message); // 会输出:Property 'city' has already been set on object of type 'BillTo'. Duplicate properties are not allowed.
}

注意事项

  • 这个方案会对所有属性生效,如果你只需要针对特定属性检查,可以在ThrowingOnDuplicatePropertyDescriptor的SetValue方法里添加属性名或者自定义特性判断。
  • 对于值类型属性(比如int、bool),如果默认值和业务场景的有效值重合,可能会被误判为已设置。你可以在PropertyTracking的标记逻辑里添加额外判断,只跟踪非默认值的赋值操作。

内容的提问来源于stack exchange,提问作者damageboy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:06:19