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

C#提取无限嵌套未知属性对象叶子值类型的最优方案咨询

C#提取无限嵌套对象的叶子值(保留重复属性名)

方案一:反射+递归实现

这种方式直接通过C#反射机制遍历对象结构,适合处理普通.NET对象(包括匿名类型、自定义类),核心是递归判断每个成员是否为目标叶子类型,若是则记录属性名和值,否则继续遍历。

需要注意处理可空值类型(如int?、decimal?)和集合类型(如List<T>、数组),避免遗漏嵌套在集合中的元素。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

public static class ObjectLeafExtractor
{
    // 定义目标叶子类型
    private static readonly HashSet<Type> _targetLeafTypes = new HashSet<Type>
    {
        typeof(string),
        typeof(int), typeof(int?),
        typeof(double), typeof(double?),
        typeof(decimal), typeof(decimal?)
    };

    public static IEnumerable<(string PropertyName, object Value)> ExtractLeafValues(object obj, string parentPropertyName = "")
    {
        if (obj == null) yield break;

        var objType = obj.GetType();
        
        // 处理集合类型(数组、List等)
        if (obj is IEnumerable enumerable && !(obj is string))
        {
            foreach (var item in enumerable)
            {
                foreach (var leaf in ExtractLeafValues(item, parentPropertyName))
                {
                    yield return leaf;
                }
            }
            yield break;
        }

        // 处理叶子类型
        var underlyingType = Nullable.GetUnderlyingType(objType) ?? objType;
        if (_targetLeafTypes.Contains(underlyingType))
        {
            yield return (parentPropertyName, obj);
            yield break;
        }

        // 处理复杂对象的属性
        foreach (PropertyInfo prop in objType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            var propValue = prop.GetValue(obj);
            var currentPropName = string.IsNullOrEmpty(parentPropertyName) 
                ? prop.Name 
                : $"{parentPropertyName}.{prop.Name}";

            foreach (var leaf in ExtractLeafValues(propValue, currentPropName))
            {
                yield return leaf;
            }
        }
    }
}

使用示例:

// 测试嵌套对象
var testObj = new 
{
    Name = "Andrew",
    Age = 30,
    Contact = new 
    {
        Phone = "123456",
        Address = new 
        {
            ZipCode = 100000,
            City = "Beijing"
        }
    },
    Scores = new List<int> { 90, 85, 95 },
    Salary = 12345.67m
};

foreach (var leaf in ObjectLeafExtractor.ExtractLeafValues(testObj))
{
    Console.WriteLine($"{leaf.PropertyName}: {leaf.Value}");
}

方案二:利用Json.NET(Newtonsoft.Json)实现

如果对象结构非常复杂(比如包含ExpandoObject、dynamic类型),用Json.NET处理会更省心,它能自动解析所有嵌套结构,包括动态对象和集合。

核心思路是将对象序列化为JToken,然后递归遍历每个JProperty和JValue,筛选出目标类型的值并保留属性名。

using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;

public static class JsonLeafExtractor
{
    public static IEnumerable<(string PropertyName, object Value)> ExtractLeafValues(object obj)
    {
        var jToken = JToken.FromObject(obj);
        return TraverseJToken(jToken);
    }

    private static IEnumerable<(string PropertyName, object Value)> TraverseJToken(JToken token, string parentPath = "")
    {
        switch (token.Type)
        {
            case JTokenType.Object:
                foreach (JProperty prop in token.Children<JProperty>())
                {
                    var currentPath = string.IsNullOrEmpty(parentPath) 
                        ? prop.Name 
                        : $"{parentPath}.{prop.Name}";
                    foreach (var leaf in TraverseJToken(prop.Value, currentPath))
                    {
                        yield return leaf;
                    }
                }
                break;
            case JTokenType.Array:
                foreach (var item in token.Children())
                {
                    foreach (var leaf in TraverseJToken(item, parentPath))
                    {
                        yield return leaf;
                    }
                }
                break;
            case JTokenType.String:
            case JTokenType.Integer:
            case JTokenType.Float:
            case JTokenType.Decimal:
                yield return (parentPath, token.Value<object>());
                break;
            // 忽略其他类型(如null、布尔值等)
            default:
                yield break;
        }
    }
}

使用示例:

// 测试动态对象
dynamic dynamicObj = new System.Dynamic.ExpandoObject();
dynamicObj.Name = "Gray";
dynamicObj.Info = new ExpandoObject();
dynamicObj.Info.Height = 175.5;
dynamicObj.Scores = new[] { 88, 92 };

foreach (var leaf in JsonLeafExtractor.ExtractLeafValues(dynamicObj))
{
    Console.WriteLine($"{leaf.PropertyName}: {leaf.Value}");
}

方案对比

  • 反射方案:无需第三方库,适合处理强类型对象,但对动态对象(ExpandoObject)的支持有限。
  • Json.NET方案:对动态对象、匿名类型、复杂嵌套结构兼容性更好,但需要引入Newtonsoft.Json NuGet包。

两种方案都会保留重复的属性名(比如嵌套结构中不同层级出现同名属性时,会分别记录完整路径和值)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 21:36:21