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

C#中通过GetType从字符串为属性赋值及类型转换问题

我之前做类似的航天器数据映射时碰到过完全一样的问题——用String.Split根本搞不定类型转换和嵌套属性的复杂场景,后来用JSON序列化框架+反射的组合完美解决了,刚好匹配你所有的需求,给你详细拆解下:

解决方案:用成熟JSON库+反射实现严格属性映射

核心思路

  • 用.NET内置的System.Text.Json(或第三方的Newtonsoft.Json)处理类型转换和嵌套对象解析,避免自己造轮子处理"1"转bool、字符串转数值这类繁琐的类型转换逻辑
  • 结合反射验证JSON中的所有键都对应类的属性(允许类有多余属性,但JSON不能出现类没有的键)
  • 通过预处理JSON移除指定的无关节点(比如你提到的_report)

步骤1:先定义你的目标类(包含嵌套属性)

比如你提到的InternationalSpaceStation和Location类,我补全了示例属性:

public class Location
{
    public string RefID { get; set; }
    public bool myNiceProp { get; set; } // 对应你提到的bool类型属性
    public double Latitude { get; set; }
}

public class InternationalSpaceStation
{
    public string StationName { get; set; }
    public Location CurrentLocation { get; set; }
    public int CrewCount { get; set; }
    // 类可以有多余属性,JSON不需要包含
    public string InternalSystemId { get; set; }
}

步骤2:实现映射工具类(包含验证、忽略节点、类型转换)

下面是完整的工具类代码,包含所有需求的实现:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;

public class StrictJsonMapper
{
    // 配置需要忽略的无关节点,可根据需求扩展
    private static readonly HashSet<string> IgnoredNodes = new HashSet<string> { "_report" };

    public static T MapToClass<T>(string json) where T : new()
    {
        // 1. 解析JSON为可操作的节点对象
        var jsonNode = JsonNode.Parse(json);
        if (jsonNode == null)
            throw new ArgumentNullException(nameof(json), "JSON字符串为空或格式无效");

        // 2. 递归移除所有无关节点
        RemoveIgnoredNodes(jsonNode);

        // 3. 严格验证JSON的所有键都存在于目标类中
        ValidateAllJsonKeys<T>(jsonNode);

        // 4. 反序列化并自动处理类型转换
        var serializerOptions = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true, // 可选:忽略属性名大小写匹配
            AllowTrailingCommas = true
        };
        return jsonNode.Deserialize<T>(serializerOptions) ?? new T();
    }

    private static void RemoveIgnoredNodes(JsonNode node)
    {
        if (node is JsonObject jsonObj)
        {
            // 移除当前层级的无关节点
            foreach (var ignoredNode in IgnoredNodes)
            {
                jsonObj.Remove(ignoredNode);
            }
            // 递归处理嵌套对象
            foreach (var property in jsonObj)
            {
                if (property.Value is JsonObject nestedObj)
                {
                    RemoveIgnoredNodes(nestedObj);
                }
            }
        }
    }

    private static void ValidateAllJsonKeys<T>(JsonNode node, string parentPath = "")
    {
        if (node is JsonObject jsonObj)
        {
            foreach (var (key, value) in jsonObj)
            {
                var fullPropertyPath = string.IsNullOrEmpty(parentPath) ? key : $"{parentPath}.{key}";
                
                // 检查当前键是否对应类的属性
                if (!DoesPropertyExist<T>(fullPropertyPath))
                {
                    throw new InvalidOperationException($"JSON中的键 '{fullPropertyPath}' 在目标类 {typeof(T).Name} 中不存在");
                }

                // 递归验证嵌套对象的键
                if (value is JsonObject nestedObj)
                {
                    var nestedType = GetNestedPropertyType<T>(fullPropertyPath);
                    if (nestedType != null)
                    {
                        // 通过反射调用泛型验证方法
                        var validateMethod = typeof(StrictJsonMapper).GetMethod(nameof(ValidateAllJsonKeys), BindingFlags.NonPublic | BindingFlags.Static);
                        var genericValidate = validateMethod?.MakeGenericMethod(nestedType);
                        genericValidate?.Invoke(null, new object[] { nestedObj, fullPropertyPath });
                    }
                }
            }
        }
    }

    // 检查指定路径的属性是否存在于类中(支持嵌套路径,如"CurrentLocation.RefID")
    private static bool DoesPropertyExist<T>(string propertyPath)
    {
        Type currentType = typeof(T);
        foreach (var propName in propertyPath.Split('.'))
        {
            var property = currentType.GetProperty(propName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
            if (property == null)
                return false;
            currentType = property.PropertyType;
        }
        return true;
    }

    // 获取嵌套属性的类型
    private static Type? GetNestedPropertyType<T>(string propertyPath)
    {
        Type currentType = typeof(T);
        foreach (var propName in propertyPath.Split('.'))
        {
            var property = currentType.GetProperty(propName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
            if (property == null)
                return null;
            currentType = property.PropertyType;
        }
        return currentType;
    }
}

步骤3:使用示例

下面是调用工具类的示例,模拟你提供的JSON场景:

public class Program
{
    public static void Main()
    {
        // 示例JSON:包含嵌套属性、需要忽略的_report节点、字符串类型的bool/数值
        string sampleJson = @"
        {
            ""StationName"": ""International Space Station"",
            ""CurrentLocation"": {
                ""RefID"": ""ISS-LOC-2024"",
                ""myNiceProp"": ""1"",
                ""Latitude"": ""41.23""
            },
            ""CrewCount"": ""7"",
            ""_report"": {
                ""GeneratedAt"": ""2024-05-20T14:30:00""
            }
        }";

        try
        {
            var iss = StrictJsonMapper.MapToClass<InternationalSpaceStation>(sampleJson);
            Console.WriteLine($"空间站名称:{iss.StationName}");
            Console.WriteLine($"myNiceProp值:{iss.CurrentLocation.myNiceProp}"); // 自动转换"1"为true
            Console.WriteLine($"纬度:{iss.CurrentLocation.Latitude}"); // 自动转换"41.23"为double
        }
        catch (Exception ex)
        {
            Console.WriteLine($"错误:{ex.Message}");
        }
    }
}

关键特性说明

  • 自动类型转换:依赖JSON序列化库的内置逻辑,自动处理字符串转bool、数值等常见类型转换,不需要手动写判断逻辑
  • 严格键验证:通过反射遍历类的所有属性(包括嵌套属性),JSON中出现类没有的键会直接抛出错误,完全符合你的要求
  • 无关节点忽略:通过IgnoredNodes配置要忽略的节点,递归移除后再进行验证和反序列化
  • 支持嵌套属性:自动处理JSON中的嵌套对象,验证嵌套类的属性合法性并完成映射

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 09:37:42