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
相关产品推荐
相关产品推荐

