博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
动态类型序列化
阅读量:7229 次
发布时间:2019-06-29

本文共 18814 字,大约阅读时间需要 62 分钟。

上一篇文章直接就被移除首页了,这次来点大家都能懂的干货.

需求

之前做一个winform的工具时候有以下几个需求

1. 主窗体(或者叫平台)可以安装若干类型的插件。
2. 插件关闭时候需要保存状态。
3. 插件加载的时候可以加载上次关闭的配置。
4. 插件中的配置可以切换。
5. 主窗体本身保存当前插件,并且可以通过不同的配置文件切换插件

使用上最方便的做法是将配置给平台来管理。但是平台本身并不知道插件要保存怎样的配置。针对以上问题在配置这个上做了如下设计

设计

1. 动态类型序列化以满足插件的任何配置需要

2. 动态类型基本的就是dynamic,那么我们需用字典作为实现
3. 支持具体的类进行序列化,那么此时需要用xml保存类的元数据信息
4. 支持接口的序列化,此时也是保存实际类型的元数据信息
5. 支持List序列化
6. 支持Arry序列化
7. 支持Dictionary序列化

接口定义

其中PathOrSourceString 属性这样既可以支持文件,也可以直接支持字符串,扩展更加方便.

public interface IConfig    {        string PathOrSourceString { get; set; }        dynamic Data { get; set; }    }

 

动态类型实现

这里是基于字典,网上有很多类似的代码。

这里字典的Value设计成dynamic是为了嵌套。

[Serializable]    public class DynamicDictionary : DynamicObject    {        private Dictionary
_dictionary = new Dictionary
(); public override bool TryGetMember(GetMemberBinder binder, out object result) { string name = binder.Name; if (!_dictionary.ContainsKey(name)) { _dictionary.Add(name, new DynamicDictionary()); } return _dictionary.TryGetValue(name, out result); } public override bool TrySetMember(SetMemberBinder binder, object value) { var key = binder.Name; if (_dictionary.ContainsKey(key)) _dictionary[key] = value; else { _dictionary.Add(key, value); } return true; } public Dictionary
Dictionary { get { return _dictionary; } } public void AddMember(string name, dynamic value) { _dictionary.Add(name, value); } }

  

配置的加载和保存逻辑(核心)

 

public static class ConfigManager    {        public static IConfig LoadFromFile(this IConfig config)        {            if (config == null || string.IsNullOrEmpty(config.PathOrSourceString))                throw new ArgumentNullException("config");            if (!File.Exists(config.PathOrSourceString))            {                return config;            }            var doc = new XmlDocument();            doc.Load(config.PathOrSourceString);            var element = doc["Data"];            config.Data = GetValue(element);            return config;        }        public static IConfig SaveToFile(this IConfig config)        {            if (config == null || string.IsNullOrEmpty(config.PathOrSourceString) || config.Data == null)                throw new ArgumentNullException("config");            var dir = Path.GetDirectoryName(config.PathOrSourceString);            if (!Directory.Exists(dir))                Directory.CreateDirectory(dir);            var doc = new XmlDocument();            doc.AppendChild(GetXml("Data", config.Data, doc));            doc.Save(config.PathOrSourceString);            return config;        }        public static IConfig LoadFromString(this IConfig config)        {            if (config == null || string.IsNullOrEmpty(config.PathOrSourceString))                throw new ArgumentNullException("config");            var doc = new XmlDocument();            doc.LoadXml(config.PathOrSourceString);            var element = doc["Data"];            config.Data = GetValue(element);            return config;        }        public static IConfig SaveToString(this IConfig config)        {            if (config == null || config.Data == null)                throw new ArgumentNullException("config");            var doc = new XmlDocument();            doc.AppendChild(GetXml("Data", config.Data, doc));            config.PathOrSourceString = doc.OuterXml;            return config;        }        #region 解析XmlElement        public static dynamic GetValue(XmlElement element)        {            if (element == null)                return null;            Classify clasify;            Enum.TryParse(element.GetAttribute("Classify"), out clasify);            switch (clasify)            {                case Classify.Sample:                    return GetSampleValue(element.GetAttribute("Assembly"), element.GetAttribute("Type"), element.InnerText);                case Classify.Array:                    return GetArrayValue(element.GetAttribute("ElementAssembly"), element.GetAttribute("ElementType"), element.GetChidlren());                case Classify.List:                    return GetListValue(element.GetAttribute("GenericAssembly"), element.GetAttribute("GenericType"), element.GetChidlren());                case Classify.Dictionary:                    return GetDictionaryValue(element.GetAttribute("KeyGenericAssembly"),                        element.GetAttribute("KeyGenericType"),                        element.GetAttribute("ValueGenericAssembly"),                        element.GetAttribute("ValueGenericType"),                        element.GetChidlren());                case Classify.Dynamic:                    return GetDynamicValue(element.GetChidlren());                case Classify.Custom:                    return GetCustomValue(element.GetAttribute("Assembly"), element.GetAttribute("Type"), element.GetChidlren());            }            return null;        }        public static object GetSampleValue(string assembly, string typeFullName, string value)        {            var type = Assembly.Load(assembly).GetType(typeFullName);            if (type == null)                return null;            return CoralConvert.Convert(value, type);        }        public static object GetListValue(string genericAssembly, string genericTypeName, List
elements) { var genericType = Assembly.Load(genericAssembly).GetType(genericTypeName); var type = typeof(List<>).MakeGenericType(genericType); dynamic list = Activator.CreateInstance(type, true); foreach (var element in elements) { list.Add(GetValue(element)); } return list; } public static object GetArrayValue(string elementAssembly, string elementTypeName, List
elements) { var elementType = Assembly.Load(elementAssembly).GetType(elementTypeName); dynamic list = Array.CreateInstance(elementType, elements.Count); for (int i = 0; i < elements.Count; i++) { list[i] = GetValue(elements[i]); } return list; } public static object GetDictionaryValue(string keyAssembly, string keyTypeName, string valueAssembly, string valueTypeName, List
elements) { var keyType = Assembly.Load(keyAssembly).GetType(keyTypeName); var valueType = Assembly.Load(valueAssembly).GetType(valueTypeName); var type = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); dynamic dict = Activator.CreateInstance(type, true); foreach (var element in elements) { dict.Add(GetValue(element["Key"]), GetValue(element["Value"])); } return dict; } public static object GetDynamicValue(List
elements) { var dict = new DynamicDictionary(); foreach (var element in elements) { dict.Dictionary.Add(GetValue(element["Key"]), GetValue(element["Value"])); } return dict; } public static object GetCustomValue(string assemblyFullName, string typeFullName, List
elements) { var type = Assembly.Load(assemblyFullName).GetType(typeFullName); if (type == null) return null; dynamic obj = Activator.CreateInstance(type, true); foreach (var element in elements) { var property = type.GetProperty(element.Name); object value; if (!CoralConvert.Convert(GetValue(element), property.PropertyType, out value)) continue; property.SetValue(obj, value); } return obj; } #endregion #region 创建XmlElement ///
/// 创建xml元素 /// ///
///
///
///
public static XmlElement GetXml(string name, object data, XmlDocument doc) { if (data == null) return null; if (data.GetType().IsValueType || data is string) { return GetValueTypeXml(name, data, doc); } var list = data as IList; if (list != null) { return GetIListXml(name, list, doc); } var dict = data as IDictionary; if (dict != null) { return GetIDictionaryXml(name, dict, doc); } var dynamic = data as DynamicDictionary; if (dynamic != null) { return GetDynamicXml(name, dynamic, doc); } return GetCustomXml(name, data, doc); } ///
/// 创建简单类型的xml元素 /// ///
///
///
///
private static XmlElement GetValueTypeXml(string name, object data, XmlDocument doc) { if (data == null) return null; var element = doc.CreateElement(name); element.SetAttribute("Type", data.GetType().FullName); element.SetAttribute("Assembly", MetaDataManager.Assembly.GetAssemblySortName(data.GetType().Assembly)); element.SetAttribute("Classify", Classify.Sample.ToString()); element.InnerText = data.ToString(); return element; } ///
/// 获取列表类型的xml /// ///
///
///
///
private static XmlElement GetIListXml(string name, object datas, XmlDocument doc) { if (datas == null) return null; var element = doc.CreateElement(name); if (datas.GetType().IsArray) { element.SetAttribute("Type", typeof(Array).FullName); element.SetAttribute("Classify", Classify.Array.ToString()); element.SetAttribute("ElementType", datas.GetType().GetElementType().FullName); element.SetAttribute("ElementAssembly", datas.GetType().GetElementType().Assembly.FullName); } else { element.SetAttribute("Type", typeof(IList).FullName); element.SetAttribute("Classify", Classify.List.ToString()); element.SetAttribute("GenericType", datas.GetType().GenericTypeArguments[0].FullName); element.SetAttribute("GenericAssembly", datas.GetType().GenericTypeArguments[0].Assembly.FullName); } foreach (var data in (IList)datas) { element.AppendChild(GetXml("Element", data, doc)); } return element; } ///
/// 创建动态类型的xml /// ///
///
///
///
private static XmlElement GetDynamicXml(string name, dynamic data, XmlDocument doc) { if (data == null) return null; var element = doc.CreateElement(name); element.SetAttribute("Type", "dynamic"); element.SetAttribute("Classify", Classify.Dynamic.ToString()); foreach (DictionaryEntry item in (IDictionary)data.Dictionary) { var child = doc.CreateElement("Element"); child.AppendChild(GetXml("Key", item.Key ?? string.Empty, doc)); child.AppendChild(GetXml("Value", item.Value ?? string.Empty, doc)); element.AppendChild(child); } return element; } ///
/// 创建字典类型的xml /// ///
///
///
///
private static XmlElement GetIDictionaryXml(string name, object datas, XmlDocument doc) { if (datas == null) return null; var element = doc.CreateElement(name); element.SetAttribute("Type", typeof(IDictionary).FullName); element.SetAttribute("Classify", Classify.Dictionary.ToString()); element.SetAttribute("KeyGenericAssembly", datas.GetType().GetGenericArguments()[0].Assembly.FullName); element.SetAttribute("KeyGenericType", datas.GetType().GetGenericArguments()[0].FullName); element.SetAttribute("ValueGenericAssembly", datas.GetType().GetGenericArguments()[1].Assembly.FullName); element.SetAttribute("ValueGenericType", datas.GetType().GetGenericArguments()[1].FullName); foreach (DictionaryEntry data in (IDictionary)datas) { var child = doc.CreateElement("Element"); child.AppendChild(GetXml("Key", data.Key ?? string.Empty, doc)); child.AppendChild(GetXml("Value", data.Value ?? string.Empty, doc)); element.AppendChild(child); } return element; } ///
/// 创建自定义类 /// ///
///
///
///
private static XmlElement GetCustomXml(string name, object data, XmlDocument doc) { if (data == null) return null; var element = doc.CreateElement(name); element.SetAttribute("Assembly",MetaDataManager.Assembly.GetAssemblySortName(data.GetType().Assembly)); element.SetAttribute("Type", data.GetType().FullName); element.SetAttribute("Classify", Classify.Custom.ToString()); data.GetType().GetProperties().ForEach(property => { var item = GetXml(property.Name, property.GetValue(data), doc); if (item != null) element.AppendChild(item); }); return element; } #endregion public enum Classify { Sample, List, Array, Dictionary, Dynamic, Custom, } public static List
GetChidlren(this XmlElement element) { return element.Cast
().ToList(); } }

  

  

核心思路就是递归,充分利用元数据

 

测试代码

public class XmlConfig : IConfig    {        public string PathOrSourceString { get; set; }        public dynamic Data { get; set; }    }    public interface ITestModel    {        string Name { get; set; }        string DataType { get; set; }        string Data { get; set; }    }    public class TestConfig    {        public ITestModel Model { get; set; }        public  List
List { get; set; } public Dictionary
Dict { get; set; } } public class TestModel: ITestModel { public string Name { get; set; } public string DataType { get; set; } public string Data { get; set; } public List
List { get; set; } public Dictionary
Dict { get; set; } } public class ConfigTest { public static void PerformanceTest() { var xmlconfig = new XmlConfig(); xmlconfig.PathOrSourceString = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs", "test1.Config"); #region list 类,字典测试 var testModel= new TestModel() { Name = "1", DataType = "1", Data = "1", List = new List
{ new TestModel { Name = "2", DataType = "2", Data = "2", }, new TestModel { Name = "3", DataType = "3", Data = "3", }, }, Dict = new Dictionary
{ {"4", new TestModel { Name = "4", DataType = "4", Data = "4", } }, {"5", new TestModel { Name = "5", DataType = "5", Data = "5", } }, } }; #endregion xmlconfig.Data = new TestConfig() { Model = testModel, Dict = new Dictionary
() { {"1",testModel }, {"2",testModel } }, List = new List
{ testModel,testModel} }; #region 动态类型,类,list,字典总和测试 xmlconfig.Data = new DynamicDictionary(); xmlconfig.Data.Name = "Test1"; xmlconfig.Data.DataType = "Test1"; xmlconfig.Data.List = new List
{ new TestModel { Name = "2", DataType = "2", Data = "2", }, new TestModel { Name = "3", DataType = "3", Data = "3", }, }; xmlconfig.Data.Dict = new Dictionary
{ { "4", new TestModel { Name = "4", DataType = "4", Data = "4", } }, { "5", new TestModel { Name = "5", DataType = "5", Data = "5", } }, }; xmlconfig.Data.Other.Name = "Test1"; xmlconfig.Data.Other.DataType = "Test1"; #endregion xmlconfig.SaveToFile(); var data = new XmlConfig(); data.PathOrSourceString = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs", "test1.Config"); data.LoadFromFile(); }

 配置文件为

  
Name
Test1
DataType
Test1
List
2
2
2
3
3
3
Dict
4
4
4
4
5
5
5
5
Other
Name
Test1
DataType
Test1

  

说明

最大的用处,你拿到一个对象未知的对象,并不需要知道他的实际类型,就可以进行持久化,并且读取出来之后能够还原到原始类型。

实现这部分我觉得在于以下几个点

1. 对元数据的充分理解

2. 对xml结构的充分理解
3. 需要一点写算法的能力

我觉得代码本身并不复杂,只要耐心单步调试都能看懂。

当然这个是有一定限制的:

1. 可读性不强,所以在需要从文件进行修改配置比较麻烦

2.不可跨系统,文件中类型从程序集加载不到时就会出错

3.性能不高.性能敏感的部分不太适合

所以这部分功能需要结合业务场景使用,在我这里,包含作业调度系统,统计系统,接口测试工具中有使用.

这其实特别想WSDL的Soap协议,文件中既包含元数据的说明,又包含数据本身.真个元数据变成也是一个做设计时候一个重要思想。

 

转载于:https://www.cnblogs.com/Skyven/p/7795535.html

你可能感兴趣的文章
20180814 基于51单片机的数码相机实验指导书编写,继续挖坑
查看>>
数据库中的T-sql语句 条件修改 高级查询
查看>>
win7开机密码忘记了
查看>>
阿里前端两年随想
查看>>
day28(ajax之js原生代码实现)
查看>>
用自定义属性attr或prop方法,遍历获取当前点击a的titleid
查看>>
安卓真机测试遇到的检测不到安卓设备的问题
查看>>
我的大学,我的梦
查看>>
洛谷训练P1008(循环+暴力)
查看>>
【挖坟】HDU3205 Factorization
查看>>
reentrantlock用于替代synchronized
查看>>
Android包管理机制(二)PackageInstaller安装APK
查看>>
测试aau代码
查看>>
jenkins相关默认路径
查看>>
条件编译#ifndef
查看>>
正则表达式
查看>>
slick对超过22个属性的表进行映射的两种办法
查看>>
hdu5731
查看>>
iOS 路径设置(转)
查看>>
科学计算和可视化
查看>>