在C#开发中,我们经常需要将Plain Old CLR Object (POCO)转换为Dictionary<string, object>
。这种需求在与第三方API交互、序列化数据、动态数据处理等场景中尤为常见。本文将深入探讨五种不同的转换方法,分析它们的特点、适用场景,并提供详细的代码示例和性能比较。
使用反射(Reflection)
特点
- 灵活性高,可以处理任何POCO对象
- 不需要额外的依赖库
- 性能较好,适合频繁使用的场景
应用场景
- 动态处理未知类型的对象
- 需要自定义属性过滤或转换逻辑
- 框架开发,需要通用的对象处理机制
代码示例
using System;
using System.Reflection;namespace AppPOCO
{publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Alice", Age = 30 };var dict = ConvertToDict(person);Console.WriteLine(dict["Name"]);Console.WriteLine(dict["Age"]);Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDict(object obj){return obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToDictionary(prop => prop.Name, prop => prop.GetValue(obj));}}
}
高级应用:属性过滤
using System;
using System.Reflection;namespace AppPOCO
{publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Alice", Age = 30 };var filteredDict = ConvertToDictFiltered(person, prop => prop.PropertyType == typeof(string));Console.WriteLine(filteredDict["Name"]);Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictFiltered(object obj, Func<PropertyInfo, bool> filter){return obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(filter).ToDictionary(prop => prop.Name, prop => prop.GetValue(obj));}}
}
使用Newtonsoft.Json
特点
- 利用成熟的JSON序列化库
- 可以处理复杂的对象结构,包括嵌套对象和集合
- 配置灵活,可以自定义序列化行为
应用场景
- 处理包含复杂数据结构的对象
- 需要在序列化和反序列化之间保持一致性
- 已经在项目中使用Newtonsoft.Json库
代码示例
using System;
using System.Net;
using System.Reflection;
using Newtonsoft.Json;
usingstatic AppPOCO.Person;namespace AppPOCO
{publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var complexPerson = new Person{Name = "Bob",Age = 35,address = new Address { Street = "123 Main St", City = "Anytown" }};var dict = ConvertToDictNewtonsoft(complexPerson);foreach (var item in dict){Console.WriteLine($"{item.Key}: {item.Value}");}Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictNewtonsoft(object obj){var json = JsonConvert.SerializeObject(obj);return JsonConvert.DeserializeObject<Dictionary<string, object>>(json);}}
}
高级应用:自定义序列化
using System;
using System.Net;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
usingstatic AppPOCO.Person;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Charlie", Gender = Gender.Male };var dict = ConvertToDictNewtonsoftCustom(person);Console.WriteLine(JsonConvert.SerializeObject(dict));Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictNewtonsoftCustom(object obj){var settings = new JsonSerializerSettings{NullValueHandling = NullValueHandling.Ignore,Converters = new List<JsonConverter> { new StringEnumConverter() }};var json = JsonConvert.SerializeObject(obj, settings);return JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings);}}
}
使用System.Text.Json(C# 8.0+)
特点
- .NET Core和.NET 5+的内置JSON库
- 性能优于Newtonsoft.Json
- 与.NET生态系统深度集成
应用场景
- 新的.NET
Core或.NET 5+项目 - 需要高性能JSON序列化
- 不需要Newtonsoft.Json的特定功能
代码示例
using System;
using System.Net;
using System.Reflection;
using System.Text.Json;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "David", Age = 40 };var dict = ConvertToDictSystemTextJson(person);Console.WriteLine(dict["Name"]);Console.WriteLine(dict["Age"]);Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictSystemTextJson(object obj){var json = JsonSerializer.Serialize(obj);return JsonSerializer.Deserialize<Dictionary<string, object>>(json);}}
}
高级应用:自定义JsonSerializerOptions
public static Dictionary<string, object> ConvertToDictSystemTextJsonCustom(object obj)
{var options = new JsonSerializerOptions {PropertyNamingPolicy = JsonNamingPolicy.CamelCase,WriteIndented = true};var json = JsonSerializer.Serialize(obj, options);return JsonSerializer.Deserialize<Dictionary<string, object>>(json, options);
}
使用LINQ和自定义逻辑
特点
- 高度可定制
- 不依赖外部库
- 可以实现复杂的转换逻辑
应用场景
- 需要对属性名或值进行特殊处理
- 只需要转换特定的属性
- 需要在转换过程中执行额外的逻辑
代码示例
using System;
using System.Net;
using System.Reflection;
using System.Text.Json;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Frank", Age = 0 };var dict = ConvertToDictLinq(person);Console.WriteLine(JsonSerializer.Serialize(dict));Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictLinq(object obj){return obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToDictionary(prop => prop.Name,prop => prop.GetValue(obj) ?? "N/A");}}
}
高级应用:自定义键值转换
using System;
using System.Net;
using System.Reflection;
using System.Text.Json;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Grace", Age = 50 };var dict = ConvertToDictLinqCustom(person);Console.WriteLine(JsonSerializer.Serialize(dict));Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictLinqCustom(object obj){return obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToDictionary(prop => prop.Name.ToLower(),prop => prop.PropertyType == typeof(string)? prop.GetValue(obj)?.ToString().ToUpper(): prop.GetValue(obj));}}
}
使用ExpandoObject
特点
- 支持动态添加和删除属性
- 可以像使用动态对象一样使用字典
- 适合需要频繁修改结构的场景
应用场景
- 动态数据处理
- 构建灵活的数据结构
- 与动态语言交互
代码示例
using System;
using System.Dynamic;
using System.Net;
using System.Reflection;
using System.Text.Json;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Henry", Age = 45 };dynamic expandoDict = ConvertToExpandoObject(person);Console.WriteLine(expandoDict.Name); // 输出: HenryexpandoDict.Job = "Developer"; // 动态添加属性Console.WriteLine(JsonSerializer.Serialize(expandoDict));Console.ReadLine();}public static dynamic ConvertToExpandoObject(object obj){var expando = new ExpandoObject();var expandoDic = (IDictionary<string, object>)expando;foreach (var prop in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)){expandoDic.Add(prop.Name, prop.GetValue(obj));}return expando;}}
}
高级应用:结合LINQ和ExpandoObject
using System;
using System.Dynamic;
using System.Net;
using System.Reflection;
using System.Text.Json;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Email { get; set; }publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Ivy", Age = 0, Email = "ivy@example.com" };dynamic filteredExpando = ConvertToExpandoObjectFiltered(person, prop => prop.GetValue(person) != null && !prop.GetValue(person).Equals(0));Console.WriteLine(JsonSerializer.Serialize(filteredExpando));Console.ReadLine();}public static dynamic ConvertToExpandoObjectFiltered(object obj, Func<PropertyInfo, bool> filter){var expando = new ExpandoObject();var expandoDic = (IDictionary<string, object>)expando;obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(filter).ToList().ForEach(prop => expandoDic.Add(prop.Name, prop.GetValue(obj)));return expando;}}
}
性能比较
根据文章中提供的基准测试结果,我们可以得出以下结论:
- 反射方法和LINQ方法性能最佳,适合高频率调用的场景。
- ExpandoObject方法性能适中,在需要动态性的场景下是个不错的选择。
- JSON序列化方法(Newtonsoft.Json和System.Text.Json)性能较低,但在处理复杂对象时更为可靠。
总结
选择合适的POCO到Dictionary的转换方法取决于多个因素:
- 性能需求
- 对象复杂度
- 动态性要求
- 项目依赖
- 特定功能需求(如自定义序列化)
在实际应用中,建议根据具体场景选择最合适的方法,并进行必要的性能测试和优化。对于大多数简单场景,反射或LINQ方法可能是最佳选择;而对于复杂对象或需要特殊处理的情况,JSON序列化方法或ExpandoObject可能更为合适。
无论选择哪种方法,都要注意处理潜在的异常情况,如空值、循环引用等,以确保代码的健壮性和可靠性。