zoukankan      html  css  js  c++  java
  • XmlAttribute与实体的转换和匹配方案(附源码)

    一、前言

    可扩展标记语言 (XML) 是具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML是用来存储数据的,重在数据本身。本文中的代码是几个月前整理的,最近几个月的时间很少写随笔,除了工作以外,主要还是忙于整理自己的框架。这篇随笔主要是针对于XML的特性Attribute与实体之间的匹配与转换,其中的内容包括反射、XML以及LinqToXml,代码的内容也是想到什么就写什么,纯属练习下手感,仅供参考。下一篇随笔将以另外的形式来转换Xml为对象实体,当然,也是以反射为主,和本随笔中的思路差不多,主要是XML的格式和解决方案不同而已。对于如何将对象转换成Xml的话,主要还是看情况,仅仅转换简单的对象的话,直接反射就可以生成。对于复杂的对象的话,可以采用dynamic,树结构和递归算法的方案来定制XML。

    二、类图设计

    该处的主要思路为:通过反射将与类名(类特性名称或者类名)的节点匹配,然后匹配属性(属性特性名称或者属性名称)值。反之,则遍历实体对象的属性,设置相应的XML。本来还想细化一下匹配与转换操作的,但是该类的EA文件不知道放在哪里了,只有设计的截图还在,XO。相关类图设计如下:

     

     PropertyAttribute主要设置属性的特性名称,用于转换过程设置属性的别名,同时匹配过程中匹配XML的特性与属性的名称。

     ClassAttribute主要设置类的特性名称,用于转换过程设置类的别名,同时匹配过程中匹配XML的节点与类的名称。

     StringExtension主要是字符串的扩展方法。

     FuncDictionary主要转换字符串为特定类型的值。

     XmlAttributeUtility主要转换实体对象为XML以及匹配XML为实体对象,其中还包括一些其他基本操作。

    三、具体实现

    先看下FuncDictionary,该类主要通过异步委托将字符串转换成相应的简单类型,所有实现围绕该类进行相关操作。FuncDictionary基本涵盖了C#中的所有简单类型,可以将字符串转换成这些简单类型。

    主要方法为:public object DynamicInvoke(Type type, string arg),通过传入的类型和字符串值,可以转换成相应的Object值。属性Dictionary中涵盖了所有简单类型转换的委托操作。代码如下:
     1     public class FuncDictionary
     2     {
     3         public static IDictionary<Type, Delegate> Dictionary
     4         {
     5             get;
     6             private set;
     7         }
     8 
     9         static FuncDictionary()
    10         {
    11             if (FuncDictionary.Dictionary == null)
    12             {
    13                 FuncDictionary.Dictionary = CreateDictionary();
    14             }
    15         }
    16 
    17         public object DynamicInvoke(Type type, string arg)
    18         {
    19             if (type == null)
    20             {
    21                 return null;
    22             }
    23 
    24             if (FuncDictionary.Dictionary == null)
    25             {
    26                 FuncDictionary.Dictionary = CreateDictionary();
    27             }
    28 
    29             if (!FuncDictionary.Dictionary.ContainsKey(type))
    30             {
    31                 return null;
    32             }
    33 
    34             Delegate action = FuncDictionary.Dictionary[type];
    35 
    36             return action.DynamicInvoke(new object[] { arg });
    37         }
    38 
    39         public static IDictionary<Type, Delegate> CreateDictionary()
    40         {
    41             var dictionary = new Dictionary<Type, Delegate>();
    42 
    43             dictionary.Add(typeof(string), new Func<stringstring>(p => p));
    44             dictionary.Add(typeof(decimal), new Func<stringdecimal>(p => p.AsDecimal()));
    45             dictionary.Add(typeof(DateTime), new Func<string, DateTime>(p => p.AsDateTime()));
    46             dictionary.Add(typeof(float), new Func<stringfloat>(p => p.AsFloat()));
    47             dictionary.Add(typeof(double), new Func<stringdouble>(p => p.AsDouble()));
    48             dictionary.Add(typeof(int), new Func<stringint>(p => p.AsInt()));
    49             dictionary.Add(typeof(byte), new Func<stringbyte>(p => p.AsByte()));
    50             dictionary.Add(typeof(sbyte), new Func<stringsbyte>(p => p.AsSbyte()));
    51             dictionary.Add(typeof(short), new Func<stringshort>(p => p.AsShort()));
    52             dictionary.Add(typeof(ushort), new Func<stringushort>(p => p.AsUshort()));
    53             dictionary.Add(typeof(uint), new Func<stringuint>(p => p.AsUint()));
    54             dictionary.Add(typeof(long), new Func<stringlong>(p => p.AsLong()));
    55             dictionary.Add(typeof(ulong), new Func<stringulong>(p => p.AsUlong()));
    56             dictionary.Add(typeof(char), new Func<stringchar>(p => p.AsChar()));
    57             dictionary.Add(typeof(bool), new Func<stringbool>(p => p.AsBool()));
    58             dictionary.Add(typeof(Color), new Func<string, Color>(p => p.AsColor()));
    59 
    60             return dictionary;
    61         }
    62     }

     再看下XmlAttributeUtility类,该类主要包括转换和匹配操作。匹配主要为两种方案(主要逻辑为以下代码的72-183行):

    1、通过XmlReader顺序读取来设置实体的值,主要方法为public static IList<T> Parse<T>(XmlReader reader) where T : new():

    2、通过遍历XmlNodeList中的节点,依次遍历节点中的XmlAttribute设置实体的属性的值,主要方法为:public static IList<T> Parse<T>(XmlNodeList nodeList) where T : new()

      1         public static IList<T> Parse<T>(string inputUri, string parentXPath) where T : new()
      2         {
      3             if (!File.Exists(inputUri) || string.IsNullOrWhiteSpace(parentXPath))
      4             {
      5                 return new List<T>();
      6             }
      7 
      8             XmlDocument document = new XmlDocument();
      9             document.Load(inputUri);
     10 
     11             return Parse<T>(document, parentXPath);
     12         }
     13 
     14         public static IList<T> Parse<T>(XmlDocument document, string parentXPath) where T : new()
     15         {
     16             if (document == null || string.IsNullOrWhiteSpace(parentXPath))
     17             {
     18                 return new List<T>();
     19             }
     20 
     21             XmlNode parentNode = document.DocumentElement.SelectSingleNode(parentXPath);
     22 
     23             if (parentNode == null)
     24             {
     25                 return new List<T>();
     26             }
     27 
     28             return Parse<T>(parentNode);
     29         }
     30 
     31         public static IList<T> Parse<T>(XmlNode parentNode) where T : new()
     32         {
     33             if (parentNode == null || !parentNode.HasChildNodes)
     34             {
     35                 return new List<T>();
     36             }
     37 
     38             XmlNodeList nodeList = parentNode.ChildNodes;
     39 
     40             return Parse<T>(nodeList);
     41         }
     42 
     43         public static IList<T> Parse<T>(XmlNodeList nodeList) where T : new()
     44         {
     45             if (nodeList == null || nodeList.Count == 0)
     46             {
     47                 return new List<T>();
     48             }
     49 
     50             List<T> entities = new List<T>();
     51             AddEntities<T>(nodeList, entities);
     52 
     53             return entities;
     54         }
     55 
     56         public static IList<T> Parse<T>(string inputUri) where T : new()
     57         {
     58             if (!File.Exists(inputUri))
     59             {
     60                 return new List<T>();
     61             }
     62 
     63             XmlReaderSettings settings = new XmlReaderSettings();
     64             settings.IgnoreComments = true;
     65             settings.IgnoreWhitespace = true;
     66 
     67             XmlReader reader = XmlReader.Create(inputUri, settings);
     68 
     69             return Parse<T>(reader);
     70         }
     71 
     72         public static IList<T> Parse<T>(XmlReader reader) where T : new()
     73         {
     74             if (reader == null)
     75             {
     76                 return new List<T>();
     77             }
     78 
     79             reader.ReadStartElement();
     80             string className = GetClassName<T>();     
     81             List<PropertyInfo> properties = typeof(T).GetProperties().ToList();
     82             List<T> entities = new List<T>();
     83             T entity = new T();
     84 
     85             while (!reader.EOF)
     86             {
     87                 if (!string.Equals(reader.Name, className) || !reader.IsStartElement())
     88                 {
     89                     reader.Read();
     90                     continue;
     91                 }
     92 
     93                 entity = new T();
     94 
     95                 if (!reader.HasAttributes)
     96                 {
     97                     entities.Add(entity);
     98                     reader.Read();
     99                     continue;
    100                 }
    101 
    102                 SetPropertyValue<T>(reader, properties, entity);
    103                 entities.Add(entity);
    104                 reader.Read();
    105             }
    106 
    107             reader.Close();
    108 
    109             return entities;
    110         }
    152 
    153         private static void AddEntities<T>(XmlNodeList nodeList,
    154             List<T> entities) where T : new()
    155         {
    156             string className = GetClassName<T>();
    157             List<PropertyInfo> properties = typeof(T).GetProperties().ToList();
    158             T entity = new T();
    159 
    160             foreach (XmlNode xmlNode in nodeList)
    161             {
    162                 XmlElement element = xmlNode as XmlElement;
    163                 if (element == null || !string.Equals(className, element.Name))
    164                 {
    165                     continue;
    166                 }
    167 
    168                 entity = new T();
    169                 if (!element.HasAttributes)
    170                 {
    171                     entities.Add(entity);
    172                     continue;
    173                 }
    174 
    175                 XmlAttributeCollection attributes = element.Attributes;
    176                 foreach (XmlAttribute attribute in attributes)
    177                 {
    178                     SetPropertyValue<T>(properties, entity, attribute.Name, attribute.Value);
    179                 }
    180 
    181                 entities.Add(entity);
    182             }
    183         }
    184 
    185         private static void SetPropertyValue<T>(XmlReader reader, 
    186             List<PropertyInfo> properties, T entity) where T : new()
    187         {
    188             while (reader.MoveToNextAttribute())
    189             {
    190                 SetPropertyValue<T>(properties, entity, reader.Name, reader.Value);
    191             }
    192         }
    193 
    194         private static void SetPropertyValue<T>(List<PropertyInfo> properties,
    195             T entity, string name, string value) where T : new()
    196         {
    197             foreach (var property in properties)
    198             {
    199                 if (!property.CanWrite)
    200                 {
    201                     continue;
    202                 }
    203 
    204                 string propertyName = GetPropertyName(property);
    205 
    206                 if (string.Equals(name, propertyName))
    207                 {
    208                     FuncDictionary action = new FuncDictionary();
    209                     object invokeResult = action.DynamicInvoke(property.PropertyType, value);
    210 
    211                     property.SetValue(entity, invokeResult, null);
    212                 }
    213             }
    214         }

    XmlAttributeUtility的转换操作相对来说比较简单,采用反射+LinqToXml转换操作就很简单了,主要实现方法为public static XElement Convert<T>(T entity) where T : new(),其他方法都是以该方法作为基础来进行操作。为什么用LinqToXml?主要原因是LinqToXml比Xml更方便,很多细节方面不需要考虑太多。代码如下:
     1         public static string ConvertToString<T>(List<T> entities) where T : new()
     2         {
     3             List<XElement> elements = Convert<T>(entities);
     4             if (elements == null || elements.Count == 0)
     5             {
     6                 return string.Empty;
     7             }
     8 
     9             StringBuilder builder = new StringBuilder();
    10             elements.ForEach(p => builder.AppendLine(p.ToString()));
    11 
    12             return builder.ToString();
    13         }
    14 
    15         public static List<XElement> Convert<T>(List<T> entities) where T : new()
    16         {
    17             if (entities == null || entities.Count == 0)
    18             {
    19                 return new List<XElement>();
    20             }
    21 
    22             List<XElement> elements = new List<XElement>();
    23             XElement element;
    24 
    25             foreach (var entity in entities)
    26             {
    27                 element = Convert<T>(entity);
    28                 if (element == null)
    29                 {
    30                     continue;
    31                 }
    32 
    33                 elements.Add(element);
    34             }
    35 
    36             return elements;
    37         }
    38 
    39         public static string ConvertToString<T>(T entity) where T : new()
    40         {
    41             XElement element = Convert<T>(entity);
    42             return element == null ? string.Empty : element.ToString();
    43         }
    44 
    45         public static XElement Convert<T>(T entity) where T : new()
    46         {
    47             if (entity == null)
    48             {
    49                 return null;
    50             }
    51 
    52             string className = GetClassName<T>();
    53             XElement element = new XElement(className);
    54 
    55             List<PropertyInfo> properties = typeof(T).GetProperties().ToList();
    56             string propertyName = string.Empty;
    57             object propertyValue = null;
    58 
    59             foreach (PropertyInfo property in properties)
    60             {
    61                 if (property.CanRead)
    62                 {
    63                     propertyName = GetPropertyName(property);
    64                     propertyValue = property.GetValue(entity, null);
    65                     if (property.PropertyType.Name == "Color")
    66                     {
    67                         propertyValue = ColorTranslator.ToHtml((Color)propertyValue);
    68                     }
    69                     element.SetAttributeValue(propertyName, propertyValue);
    70                 }
    71             }
    72 
    73             return element;
    74         }

    该类中还包括其他的一些操作,此处不再概述,详细参见源码。

    四、单元测试

     FuncDictionary的单元测试必须涵盖所有类型的测试才能将代码覆盖率达到100%,此处只针对DateTime做正常测试、异常测试和空值测试(当然,对于其他类型的方法,可能还需要做脚本测试,SQL注入测试等,这三种类型的测试是最基本的测试),主要测试代码如下:

     1         [TestMethod()]
     2         public void DynamicInvokeTest()
     3         {
     4             FuncDictionary target = new FuncDictionary();
     5             Type type = typeof(DateTime);
     6             string arg = new DateTime(201195181818).ToString();
     7 
     8             DateTime result = (DateTime)target.DynamicInvoke(type, arg);
     9             Assert.AreEqual(result.Year, 2011);
    10             Assert.AreEqual(result.Month, 9);
    11             Assert.AreEqual(result.Day, 5);
    12             Assert.AreEqual(result.Hour, 18);
    13             Assert.AreEqual(result.Minute, 18);
    14             Assert.AreEqual(result.Second, 18);
    15         }
    16 
    17         [TestMethod()]
    18         public void DynamicInvokeWithNullOrEmptyArgsTest()
    19         {
    20             FuncDictionary target = new FuncDictionary();
    21 
    22             object result = target.DynamicInvoke(typeof(DateTime), null);
    23             Assert.AreEqual((DateTime)result,DateTime.MinValue);
    24 
    25             result = target.DynamicInvoke(typeof(DateTime), string.Empty);
    26             Assert.AreEqual((DateTime)result, DateTime.MinValue);
    27 
    28             result = target.DynamicInvoke(nullstring.Empty);
    29             Assert.AreEqual(result, null);
    30         }
    31 
    32         [TestMethod()]
    33         public void DynamicInvokeWithInvalidArgsTest()
    34         {
    35             FuncDictionary target = new FuncDictionary();
    36 
    37             object result = target.DynamicInvoke(typeof(DateTime), "w007");
    38             Assert.AreEqual((DateTime)result, DateTime.MinValue);
    39         }

    其他代码的单元测试详细见源代码,也仅仅只做了些基本的测试,写测试比写代码费哥的时间,

    五、总结

    以上的代码仅仅是当时想着怎么实现就怎么写的,完全是随意而写。仅供参考,实战没有多大意义,纯粹练习下灵感和手感,增强对技术的敏感性而已,纯属娱乐。对于

    <Lexer LexerName="Name0" FontColor="#EE2BA9" CreatedTime="2011-10-25T21:16:18.7866084+08:00" Count="0" Exist="true" LineCommentPrefix="LineCommentPrefix0" StreamCommentPrefix="StreamCommentPrefix0" StreamCommentSuffix="StreamCommentSuffix0" />此类格式的Xml转换和匹配,以上的代码完全能够满足该需求。下一篇将讲述另外一种格式的匹配,不过也是通过反射和XmlReader来进行匹配的。
    今天也是2011年最后一天,明天就是2012年了,在此恭祝大家2012元旦快乐,新一年,日子顺心多平安;新一年,祝福多多又暖暖;新一年,愿望件件都圆满;新一年,幸福快乐早实现;新一年,元旦祝福围身边;玛雅终结之年到了,所以,为了拯救人类,一定要快乐哦!

    源码下载:XmlAttribute转换和匹配源代码

  • 相关阅读:
    ABP.NET 方法使用 HTTPPOST,HTTPGET,HTTPPUT 特性
    Asp.Net Core 获取配置系统的链接字符串
    ABP.NET CORE 框架 取消新增用户邮箱地址必填验证
    前端开发常用组件库
    微信RSA加密公钥API
    动手实现一个较为简单的MQTT服务端和客户端
    计算入职时间的时间算法
    Windows Terminal 安装及美化
    Windows10内置Linux子系统(WSL)安装
    分享powershell设定网卡,ip,网关,dns的命令
  • 原文地址:https://www.cnblogs.com/jasenkin/p/xmlattribute_entity_parse_and_convert.html
Copyright © 2011-2022 走看看