程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C#中XmlAttribute與實體的轉換和匹配方案

C#中XmlAttribute與實體的轉換和匹配方案

編輯:關於C#

一、前言

可擴展標記語言 (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中涵蓋了所有簡單類型轉換的委托操作。代碼如下:

public class FuncDictionary
    {
        public static IDictionary<Type, Delegate> Dictionary
        {
            get;
            private set;
        }
       
        static FuncDictionary()
        {
            if (FuncDictionary.Dictionary == null)
            {
                FuncDictionary.Dictionary = CreateDictionary();
            }
        }
       
        public object DynamicInvoke(Type type, string arg)
        {
            if (type == null)
            {
                return null;
            }
       
            if (FuncDictionary.Dictionary == null)
            {
                FuncDictionary.Dictionary = CreateDictionary();
            }
       
            if (!FuncDictionary.Dictionary.ContainsKey(type))
            {
                return null;
            }
       
            Delegate action = FuncDictionary.Dictionary[type];
       
            return action.DynamicInvoke(new object[] { arg });
        }
       
        public static IDictionary<Type, Delegate> CreateDictionary()
        {
            var dictionary = new Dictionary<Type, Delegate>();
       
            dictionary.Add(typeof(string), new Func<string, string>(p => p));
            dictionary.Add(typeof(decimal), new Func<string, decimal>(p => p.AsDecimal()));
            dictionary.Add(typeof(DateTime), new Func<string, DateTime>(p => p.AsDateTime()));
            dictionary.Add(typeof(float), new Func<string, float>(p => p.AsFloat()));
            dictionary.Add(typeof(double), new Func<string, double>(p => p.AsDouble()));
            dictionary.Add(typeof(int), new Func<string, int>(p => p.AsInt()));
            dictionary.Add(typeof(byte), new Func<string, byte>(p => p.AsByte()));
            dictionary.Add(typeof(sbyte), new Func<string, sbyte>(p => p.AsSbyte()));
            dictionary.Add(typeof(short), new Func<string, short>(p => p.AsShort()));
            dictionary.Add(typeof(ushort), new Func<string, ushort>(p => p.AsUshort()));
            dictionary.Add(typeof(uint), new Func<string, uint>(p => p.AsUint()));
            dictionary.Add(typeof(long), new Func<string, long>(p => p.AsLong()));
            dictionary.Add(typeof(ulong), new Func<string, ulong>(p => p.AsUlong()));
            dictionary.Add(typeof(char), new Func<string, char>(p => p.AsChar()));
            dictionary.Add(typeof(bool), new Func<string, bool>(p => p.AsBool()));
            dictionary.Add(typeof(Color), new Func<string, Color>(p => p.AsColor()));
       
            return dictionary;
        }
    }

再看下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()

public static IList<T> Parse<T>(string inputUri, string parentXPath) where T : new()
        {
            if (!File.Exists(inputUri) || string.IsNullOrWhiteSpace(parentXPath))
            {
                return new List<T>();
            }
       
            XmlDocument document = new XmlDocument();
            document.Load(inputUri);
       
            return Parse<T>(document, parentXPath);
        }
       
        public static IList<T> Parse<T>(XmlDocument document, string parentXPath) where T : new()
        {
            if (document == null || string.IsNullOrWhiteSpace(parentXPath))
            {
                return new List<T>();
            }
       
            XmlNode parentNode = document.DocumentElement.SelectSingleNode(parentXPath);
       
            if (parentNode == null)
            {
                return new List<T>();
            }
       
            return Parse<T>(parentNode);
        }
       
        public static IList<T> Parse<T>(XmlNode parentNode) where T : new()
        {
            if (parentNode == null || !parentNode.HasChildNodes)
            {
                return new List<T>();
            }
       
            XmlNodeList nodeList = parentNode.ChildNodes;
       
            return Parse<T>(nodeList);
        }
       
        public static IList<T> Parse<T>(XmlNodeList nodeList) where T : new()
        {
            if (nodeList == null || nodeList.Count == 0)
            {
                return new List<T>();
            }
       
            List<T> entities = new List<T>();
            AddEntities<T>(nodeList, entities);
       
            return entities;
        }
       
        public static IList<T> Parse<T>(string inputUri) where T : new()
        {
            if (!File.Exists(inputUri))
            {
                return new List<T>();
            }
       
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.IgnoreComments = true;
            settings.IgnoreWhitespace = true;
       
            XmlReader reader = XmlReader.Create(inputUri, settings);
       
            return Parse<T>(reader);
        }
       
        public static IList<T> Parse<T>(XmlReader reader) where T : new()
        {
            if (reader == null)
            {
                return new List<T>();
            }
       
            reader.ReadStartElement();
            string className = GetClassName<T>();     
            List<PropertyInfo> properties = typeof(T).GetProperties().ToList();
            List<T> entities = new List<T>();
            T entity = new T();
       
            while (!reader.EOF)
            {
                if (!string.Equals(reader.Name, className) || !reader.IsStartElement())
                {
                    reader.Read();
                    continue;
                }
       
                entity = new T();
       
                if (!reader.HasAttributes)
                {
                    entities.Add(entity);
                    reader.Read();
                    continue;
                }
       
                SetPropertyValue<T>(reader, properties, entity);
                entities.Add(entity);
                reader.Read();
            }
       
            reader.Close();
       
            return entities;
        }
       
        private static void AddEntities<T>(XmlNodeList nodeList,
            List<T> entities) where T : new()

        {
            string className = GetClassName<T>();
            List<PropertyInfo> properties = typeof(T).GetProperties().ToList();
            T entity = new T();
       
            foreach (XmlNode xmlNode in nodeList)
            {
                XmlElement element = xmlNode as XmlElement;
                if (element == null || !string.Equals(className, element.Name))
                {
                    continue;
                }
       
                entity = new T();
                if (!element.HasAttributes)
                {
                    entities.Add(entity);
                    continue;
                }
       
                XmlAttributeCollection attributes = element.Attributes;
                foreach (XmlAttribute attribute in attributes)
                {
                    SetPropertyValue<T>(properties, entity, attribute.Name, attribute.Value);
                }
       
                entities.Add(entity);
            }
        }
       
        private static void SetPropertyValue<T>(XmlReader reader, 
            List<PropertyInfo> properties, T entity) where T : new()
        {
            while (reader.MoveToNextAttribute())
            {
                SetPropertyValue<T>(properties, entity, reader.Name, reader.Value);
            }
        }
       
        private static void SetPropertyValue<T>(List<PropertyInfo> properties,
            T entity, string name, string value) where T : new()
        {
            foreach (var property in properties)
            {
                if (!property.CanWrite)
                {
                    continue;
                }
       
                string propertyName = GetPropertyName(property);
       
                if (string.Equals(name, propertyName))
                {
                    FuncDictionary action = new FuncDictionary();
                    object invokeResult = action.DynamicInvoke(property.PropertyType, value);
       
                    property.SetValue(entity, invokeResult, null);
                }
            }
        }

XmlAttributeUtility的轉換操作相對來說比較簡單,采用反射+LinqToXml轉換操作就很簡單了,主要實現方法為public static XElement Convert<T>(T entity) where T : new(),其他方法都是以該方法作為基礎來進行操作。為什麼用LinqToXml?主要原因是LinqToXml比Xml更方便,很多細節方面不需要考慮太多。代碼如下:

public static string ConvertToString<T>(List<T> entities) where T : new()
        {
            List<XElement> elements = Convert<T>(entities);
            if (elements == null || elements.Count == 0)
            {
                return string.Empty;
            }
       
            StringBuilder builder = new StringBuilder();
            elements.ForEach(p => builder.AppendLine(p.ToString()));
       
            return builder.ToString();
        }
       
        public static List<XElement> Convert<T>(List<T> entities) where T : new()
        {
            if (entities == null || entities.Count == 0)
            {
                return new List<XElement>();
            }
       
            List<XElement> elements = new List<XElement>();
            XElement element;
       
            foreach (var entity in entities)
            {
                element = Convert<T>(entity);
                if (element == null)
                {
                    continue;
                }
       
                elements.Add(element);
            }
       
            return elements;
        }
       
        public static string ConvertToString<T>(T entity) where T : new()
        {
            XElement element = Convert<T>(entity);
            return element == null ? string.Empty : element.ToString();
        }
       
        public static XElement Convert<T>(T entity) where T : new()
        {
            if (entity == null)
            {
                return null;
            }
       
            string className = GetClassName<T>();
            XElement element = new XElement(className);
       
            List<PropertyInfo> properties = typeof(T).GetProperties().ToList();
            string propertyName = string.Empty;
            object propertyValue = null;
       
            foreach (PropertyInfo property in properties)
            {
                if (property.CanRead)
                {
                    propertyName = GetPropertyName(property);
                    propertyValue = property.GetValue(entity, null);
                    if (property.PropertyType.Name == "Color")
                    {
                        propertyValue = ColorTranslator.ToHtml((Color)propertyValue);
                    }
                    element.SetAttributeValue(propertyName, propertyValue);
                }
            }
       
            return element;
        }

該類中還包括其他的一些操作,此處不再概述,詳細參見源碼。

四、單元測試

FuncDictionary的單元測試必須涵蓋所有類型的測試才能將代碼覆蓋率達到100%,此處只針對DateTime做正常測試、異常測試和空值測試(當然,對於其他類型的方法,可能還需要做腳本測試,SQL注入測試等,這三種類型的測試是最基本的測試),主要測試代碼如下:

[TestMethod()]
        public void DynamicInvokeTest()
        {
            FuncDictionary target = new FuncDictionary();
            Type type = typeof(DateTime);
            string arg = new DateTime(2011, 9, 5, 18, 18, 18).ToString();
       
            DateTime result = (DateTime)target.DynamicInvoke(type, arg);
            Assert.AreEqual(result.Year, 2011);
            Assert.AreEqual(result.Month, 9);
            Assert.AreEqual(result.Day, 5);
            Assert.AreEqual(result.Hour, 18);
            Assert.AreEqual(result.Minute, 18);
            Assert.AreEqual(result.Second, 18);
        }
       
        [TestMethod()]
        public void DynamicInvokeWithNullOrEmptyArgsTest()
        {
            FuncDictionary target = new FuncDictionary();
       
            object result = target.DynamicInvoke(typeof(DateTime), null);
            Assert.AreEqual((DateTime)result,DateTime.MinValue);
       
            result = target.DynamicInvoke(typeof(DateTime), string.Empty);
            Assert.AreEqual((DateTime)result, DateTime.MinValue);
       
            result = target.DynamicInvoke(null, string.Empty);
            Assert.AreEqual(result, null);
        }
       
        [TestMethod()]
        public void DynamicInvokeWithInvalidArgsTest()
        {
            FuncDictionary target = new FuncDictionary();
       
            object result = target.DynamicInvoke(typeof(DateTime), "w007");
            Assert.AreEqual((DateTime)result, DateTime.MinValue);
        }

其他代碼的單元測試詳細見源代碼,也僅僅只做了些基本的測試,寫測試比寫代碼費哥的時間。

五、總結

以上的代碼僅僅是當時想著怎麼實現就怎麼寫的,完全是隨意而寫。僅供參考,實戰沒有多大意義,純粹練習下靈感和手感,增強對技術的敏感性而已,純屬娛樂。對於

<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來進行匹配的。

源碼下載:XmlAttribute轉換和匹配源代碼

http://files.cnblogs.com/jasenkin/Jasen.Framework.XmlAttr.rar

作者:JasenKin
出處:http://www.cnblogs.com/jasenkin/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved