對於XML的內容,我這邊的處理方式是將它反序列化成實體對象,畢竟操作一 個實體對象比一大堆的XPath強多了。
.net framework自帶的XML序列化和反序列化類 System.Xml.Serialization.XmlSerializer由於內部實現過於復雜,導致性能不 佳。我這邊自己實現了一個XML反序列化類,性能雖好但比較有針對性,所以今 天還是以.net framework自帶的XML反序列化類作為示范。
比如說一個XML的內容是這樣:
<? xml version = "1.0" encoding = "utf-8"?>
<OrderRequest>
<!-- 訂單號 -->
<OrderNo>T-1234567</OrderNo>
<!-- 商品名稱 -->
<CommodityName>筆記本電腦</CommodityName>
<!-- 商品數量 -->
<CommodityAmount>1</CommodityAmount>
<!-- 商品重量 單位:KG -->
<CommodityWeight>5.27</CommodityWeight>
<!-- 商品價格 -->
<CommodityValue>13999.00</CommodityValue>
<!-- 希望到達時間 -->
<HopeArriveTime>2010-09-01 00:00:00</HopeArriveTime>
<!-- 結算方式 只能為現結、到付和月結三種 -->
<PayMent>現結</PayMent>
<!-- 備注 -->
<Remark>小心輕放</Remark>
</OrderRequest>
當然,正常的訂單不會只有這麼點內容,下面我們要為它設計一個實體 類:
/// <summary>
/// 訂單實體類
/// </summary>
public class OrderRequest
{
/// <summary>
/// 訂單號
/// </summary>
public string OrderNo { get; set; }
/// <summary>
/// 商品名稱
/// </summary>
public string CommodityName { get; set; }
/// <summary>
/// 商品數量
/// </summary>
public string CommodityAmount { get; set; }
/// <summary>
/// 商品重量
/// </summary>
public string CommodityWeight { get; set; }
/// <summary>
/// 商品價格
/// </summary>
public string CommodityValue { get; set; }
/// <summary>
/// 希望到貨時間
/// </summary>
public string HopeArriveTime { get; set; }
/// <summary>
/// 結算方式
/// </summary>
public string PayMent { get; set;}
/// <summary>
/// 備注
/// </summary>
public string Remark { get; set; }
}
可能有的朋友會說,你這也忒不專業了,所有類型都是string。別著急,我 想說的重點是驗證方式,類型轉換之類的先放一邊吧。
然後利用微軟自帶的XML反序列化類將XML反序列成對象,下面是反序列化的 方法:
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
/// <summary>
/// 反序列化類
/// </summary>
public class XmlAntiSerialization
{
/// <summary>
/// 反序列化
/// </summary>
/// <param name="type">實體對象類型 </param>
/// <param name="xml">XML字符串 </param>
/// <returns></returns>
public static object Deserialize(Type type, string xml)
{
try
{
using (StringReader sr = new StringReader(xml))
{
XmlSerializer xmldes = new XmlSerializer(type);
return xmldes.Deserialize(sr);
}
}
catch(Exception ex)
{
throw ex;
}
}
}
只需如此調用即可將XML內容反序列化:
OrderRequest orderRequest = XmlAntiSerialization.Deserialize(typeof(OrderRequest), this.XmlString) as OrderRequest;
傳統的驗證方式大概是這樣的:
if (string.IsNullOrEmpty(orderRequest.OrderNo))
return "訂單號不能為空";
if(string.IsNullOrEmpty(orderRequest.CommodityAmount) || !System.Text.RegularExpressions.Regex.IsMatch (orderRequest.CommodityAmount,@"^\d+$"))
return "商品數量不能為空且只能為數 字";
if (string.IsNullOrEmpty(orderRequest.PayMent) || (orderRequest.PayMent != "現結" && orderRequest.PayMent != "到付" && orderRequest.PayMent != "月結"))
return "結算方式不能為空,且必須為 現結、到付或月結的一種";
如此這般一大堆,想保證頁面的清爽是不大可能了,按老趙的話說就是一股 語法噪音。但C sharp 這把鋒利的刃還給我們提供了更趁手的武器:特性 (Attribute)
好吧,讓我們先定義一個驗證方式的枚舉:
/// <summary>
/// 驗證類型
/// </summary>
[Flags]
public enum ValidateType
{
/// <summary>
/// 字段或屬性是否為空字串
/// </summary>
IsEmpty = 0x0001,
/// <summary>
/// 字段或屬性的最小長度
/// </summary>
MinLength = 0x0002,
/// <summary>
/// 字段或屬性的最大長度
/// </summary>
MaxLength = 0x0004,
/// <summary>
/// 字段或屬性的值是否為數值型
/// </summary>
IsNumber = 0x0008,
/// <summary>
/// 字段或屬性的值是否為時間類型
/// </summary>
IsDateTime = 0x0010,
/// <summary>
/// 字段或屬性的值是否為正確的浮點類型
/// </summary>
IsDecimal = 0x0020,
/// <summary>
/// 字段或屬性的值是否包含在指定的數據源數組中
/// </summary>
IsInCustomArray = 0x0040,
/// <summary>
/// 字段或屬性的值是否為固定電話號碼格式
/// </summary>
IsTelphone = 0x0080,
/// <summary>
/// 字段或屬性的值是否為手機號碼格式
/// </summary>
IsMobile = 0x0100
}
再實現一個自定義的特性類:
/// <summary>
/// 為元素添加驗證信息的特性類
/// </summary>
[AttributeUsage(AttributeTargets.All)]
public class ValidateAttribute : Attribute
{
/// <summary>
/// 驗證類型
/// </summary>
private ValidateType _validateType;
/// <summary>
/// 最小長度
/// </summary>
private int _minLength;
/// <summary>
/// 最大長度
/// </summary>
private int _maxLength;
/// <summary>
/// 自定義數據源
/// </summary>
private string[] _customArray;
/// <summary>
/// 驗證類型
/// </summary>
public ValidateType ValidateType
{
get { return this._validateType; }
}
/// <summary>
/// 最小長度
/// </summary>
public int MinLength
{
get { return this._minLength; }
set { this._minLength = value; }
}
/// <summary>
/// 最大長度
/// </summary>
public int MaxLength
{
get { return this._maxLength; }
set { this._maxLength = value; }
}
/// <summary>
/// 自定義數據源
/// </summary>
public string[] CustomArray
{
get { return this._customArray; }
set { this._customArray = value; }
}
/// <summary>
/// 指定采取何種驗證方式來驗證元素的有效性
/// </summary>
/// <param name="validateType"></param>
public ValidateAttribute(ValidateType validateType)
{
this._validateType = validateType;
}
}
下面我們就可以在實體類的屬性上增加特性驗證:
public class OrderRequest
{
/// <summary>
/// 訂單號
/// </summary>
[Validate(ValidateType.IsEmpty)]
public string OrderNo { get; set; }
/// <summary>
/// 商品名稱
/// </summary>
[Validate (ValidateType.IsEmpty|ValidateType.MaxLength,MaxLength = 50)]
public string CommodityName { get; set; }
/// <summary>
/// 商品數量
/// </summary>
[Validate (ValidateType.IsEmpty|ValidateType.IsNumber)]
public string CommodityAmount { get; set; }
/// <summary>
/// 商品重量
/// </summary>
[Validate(ValidateType.IsEmpty | ValidateType.IsDecimal)]
public string CommodityWeight { get; set; }
/// <summary>
/// 商品價格
/// </summary>
[Validate(ValidateType.IsEmpty | ValidateType.IsDecimal)]
public string CommodityValue { get; set; }
/// <summary>
/// 希望到貨時間
/// </summary>
[Validate(ValidateType.IsEmpty | ValidateType.IsDateTime)]
public string HopeArriveTime { get; set; }
/// <summary>
/// 結算方式
/// </summary>
[Validate(ValidateType.IsEmpty | ValidateType.IsInCustomArray,CustomArray = new string[]{"現結","到 付","月結"})]
public string PayMent { get; set;}
/// <summary>
/// 備注
/// </summary>
[Validate(ValidateType.MaxLength,MaxLength = 256)]
public string Remark { get; set; }
}
由於我們的枚舉實用了位標記(FlagsAttribute),所以我們可以對某個元素 使用多種驗證方式。下面就是驗證的實現:
/// <summary>
/// 驗證實體對象的所有帶驗證特性的元素 並返回驗證結果 如果返回結果為String.Empty 則說明元素符合驗證要求
/// </summary>
/// <param name="entityObject">實體對象 </param>
/// <returns></returns>
public static string GetValidateResult (object entityObject)
{
if (entityObject == null)
throw new ArgumentNullException("entityObject");
Type type = entityObject.GetType ();
PropertyInfo[] properties = type.GetProperties();
string validateResult = string.Empty;
foreach (PropertyInfo property in properties)
{
//獲取驗證特性
object[] validateContent = property.GetCustomAttributes(typeof(ValidateAttribute), true);
if (validateContent != null)
{
//獲取屬性的值
object value = property.GetValue(entityObject, null);
foreach (ValidateAttribute validateAttribute in validateContent)
{
switch (validateAttribute.ValidateType)
{
//驗證元素是否為空字串
case ValidateType.IsEmpty:
if (null == value || value.ToString().Length < 1)
validateResult = string.Format("元素 {0} 不 能為空", property.Name);
break;
//驗證元素的長度是否小於指定最小長度
case ValidateType.MinLength:
if (null == value || value.ToString().Length < 1) break;
if (value.ToString().Length < validateAttribute.MinLength)
validateResult = string.Format("元素 {0} 的 長度不能小於 {1}", property.Name, validateAttribute.MinLength);
break;
//驗證元素的長度是否大於指定最大長度
case ValidateType.MaxLength:
if (null == value || value.ToString().Length < 1) break;
if (value.ToString().Length > validateAttribute.MaxLength)
validateResult = string.Format("元素 {0} 的 長度不能大於{1}", property.Name, validateAttribute.MaxLength);
break;
//驗證元素的長度是否符合指定的最大長度和最小長度的范圍
case ValidateType.MinLength | ValidateType.MaxLength:
if (null == value || value.ToString().Length < 1) break;
if (value.ToString().Length > validateAttribute.MaxLength || value.ToString().Length < validateAttribute.MinLength)
validateResult = string.Format("元素 {0} 不 符合指定的最小長度和最大長度的范圍,應該在 {1} 與 {2} 之間", property.Name, validateAttribute.MinLength, validateAttribute.MaxLength);
break;
//驗證元素的值是否為值類型
case ValidateType.IsNumber:
if (null == value || value.ToString().Length < 1) break;
if (!System.Text.RegularExpressions.Regex.IsMatch (value.ToString(), @"^\d+$"))
validateResult = string.Format("元素 {0} 的 值不是值類型", property.Name);
break;
//驗證元素的值是否為正確的時間格式
case ValidateType.IsDateTime:
if (null == value || value.ToString().Length < 1) break;
if (!System.Text.RegularExpressions.Regex.IsMatch (value.ToString(), @"(\d{2,4})[-/]?([0]?[1-9]|[1][12])[-/]?([0][1-9] |[12]\d|[3][01])\s*([01]\d|[2][0-4])?[:]?([012345]?\d)?[:]?([012345]? \d)?"))
validateResult = string.Format("元素 {0} 不 是正確的時間格式", property.Name);
break;
//驗證元素的值是否為正確的浮點格式
case ValidateType.IsDecimal:
if (null == value || value.ToString().Length < 1) break;
if (!System.Text.RegularExpressions.Regex.IsMatch (value.ToString(), @"^\d+[.]?\d+$"))
validateResult = string.Format("元素 {0} 不 是正確的金額格式", property.Name);
break;
//驗證元素的值是否在指定的數據源中
case ValidateType.IsInCustomArray:
if (null == value || value.ToString().Length < 1) break;
if (null == validateAttribute.CustomArray || validateAttribute.CustomArray.Length < 1)
validateResult = string.Format("系統內部錯誤 :元素 {0} 指定的數據源為空或沒有數據", property.Name);
bool isHas = Array.Exists<string> (validateAttribute.CustomArray, delegate(string str)
{
return str == value.ToString();
}
);
if (!isHas)
validateResult = string.Format("元素 {0} 的 值設定不正確 , 應該為 {1} 中的一種", property.Name, string.Join (",", validateAttribute.CustomArray));
break;
//驗證元素的值是否為固定電話號碼格式
case ValidateType.IsTelphone:
if (null == value || value.ToString().Length < 1) break;
if (!System.Text.RegularExpressions.Regex.IsMatch (value.ToString(), @"^(\d{3,4}-)?\d{6,8}$"))
validateResult = string.Format("元素 {0} 不 是正確的固定電話號碼格式", property.Name);
break;
//驗證元素的值是否為手機號碼格式
case ValidateType.IsMobile:
if (null == value || value.ToString().Length < 1) break;
if (!System.Text.RegularExpressions.Regex.IsMatch (value.ToString(), @"^[1]+[3,5]+\d{9}$"))
validateResult = string.Format("元素 {0} 不 是正確的手機號碼格式", property.Name);
break;
//驗證元素是否為空且符合指定的最小長度
case ValidateType.IsEmpty | ValidateType.MinLength:
if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;
goto case ValidateType.MinLength;
//驗證元素是否為空且符合指定的最大長度
case ValidateType.IsEmpty | ValidateType.MaxLength:
if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;
goto case ValidateType.MaxLength;
//驗證元素是否為空且符合指定的長度范圍
case ValidateType.IsEmpty | ValidateType.MinLength | ValidateType.MaxLength:
if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;
goto case ValidateType.MinLength | ValidateType.MaxLength;
//驗證元素是否為空且值為數值型
case ValidateType.IsEmpty | ValidateType.IsNumber:
if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;
goto case ValidateType.IsNumber;
//驗證元素是否為空且值為浮點型
case ValidateType.IsEmpty | ValidateType.IsDecimal:
if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;
goto case ValidateType.IsDecimal;
//驗證元素是否為空且值為時間類型
case ValidateType.IsEmpty | ValidateType.IsDateTime:
if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;
goto case ValidateType.IsDateTime;
//驗證元素是否為空且值在指定的數據源中
case ValidateType.IsEmpty | ValidateType.IsInCustomArray:
if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;
goto case ValidateType.IsInCustomArray;
//驗證元素是否為空且值為固定電話號碼格式
case ValidateType.IsEmpty | ValidateType.IsTelphone:
if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;
goto case ValidateType.IsTelphone;
//驗證元素是否為空且值為手機號碼格式
case ValidateType.IsEmpty | ValidateType.IsMobile:
if (null == value || value.ToString().Length < 1) goto case ValidateType.IsEmpty;
goto case ValidateType.IsMobile;
default:
break;
}
}
}
if (!string.IsNullOrEmpty (validateResult))
break;
}
return validateResult;
}
最後,我們只需調用這麼一句代碼,就可以實現對整個實體類的元素的驗證 :
//驗證訂單
string checkMessage = AttributeHandle.GetValidateResult (orderRequest);
if(!string.IsNullOrEmpty(checkMessage))
return checkMessage;
//do something....
大功告成,整個頁面清爽無比
附:由於公司的一些原因,我只能在基於.net framework2.0的VS2005上開發 ,是故後面的實現代碼有些冗長