在 .NET 2.0 下加速您的 XML 开发






4.83/5 (11投票s)
2007年3月8日
4分钟阅读

39642
关于以懒惰方式使用 XML 的一篇文章。
引言
XML 标准如今已得到广泛应用。然而,仍有许多人使用 XML API 来处理 XML。这非常繁琐且容易出错。本文将展示如何在新建项目或迁移旧项目时平滑地集成 XML 功能。
第一部分:读写 XML
.NET 引入了一个名为 XmlSerializer 的类,它可以将对象序列化为 XML,反之亦然。让我们来了解一下 XML 文档。它必须包含一个根元素,这对应于 .NET 中的 XmlRoot 属性。XML 文档可能包含元素、属性、文本等。反过来,它们对应于 .NET 框架中的 XmlElement、XmlAttribute、XmlText 类。
请看下图,我们将用它来进行演示
首先,我们来实现 Corp 类的序列化。我们需要创建一个 XmlSerializer 实例,然后使用 XmlTextWriter 来对其进行序列化。
我们在 datadef.cs 中定义了结构,内容如下:
using System; using System.Collections.Generic; using System.Text; using System.Xml; using System.Xml.Serialization; namespace XMLDemo { public class Person { private String _FirstName; private String _LastName; private String _Sex; private String _SecuritySN; private String _Address; private String _Email; public String FirstName { get { return this._FirstName; } set { this._FirstName = value; } } public String LastName { get { return this._LastName; } set { this._LastName = value; } } public String Sex { get { return this._Sex; } set { this._Sex = value; } } public String SecuritySN { get { return this._SecuritySN; } set { this._SecuritySN = value; } } public String Address { get { return this._Address; } set { this._Address = value; } } public String Email { get { return this._Email; } set { this._Email = value; } } }; public class Department { private String _Name; private int _DepartmentID; private Person[] _DeptPersons; [XmlAttribute] public String Name { get { return this._Name; } set { this._Name = value; } } [XmlAttribute] public int DepartmentID { get { return this._DepartmentID; } set { this._DepartmentID = value; } } [XmlElement(ElementName = "Person")] public Person[] DeptPersons { get { return this._DeptPersons; } set { this._DeptPersons = value; } } public Person this[int i] { get { return DeptPersons[i]; } set { DeptPersons[i] = value; } } }; public class Corp { private String _Name; private Department _Dept; [XmlAttribute] public String Name { get { return this._Name; } set { this._Name = value; } } [XmlElement(ElementName = "Department")] public Department Dept { get { return this._Dept; } set { this._Dept = value; } } }; }
当 .NET 处理序列化时,它只会处理公共访问的成员,字段或属性都可以。如果您不希望某个成员被序列化,请添加 XmlIgnore .NET 属性。默认情况下,序列化会将成员视为元素,因此如果您想添加一个属性,应该添加 XmlAttribute .NET 属性。同样,序列化会以成员名作为元素名或属性名,因此如果您想更改名称,应该在 .NET 属性声明中更改名称,如上面的演示。这也意味着,如果您添加或删除某些元素,您无需更改任何代码,这是与传统方法相比的优势。最有趣的是(请看红色下划线代码),您还可以直接将数组声明为元素。
这是序列化 Corp 类的演示代码:
public static void RunDemo1() { Corp c = new Corp(); c.Name = "ChinaCars"; c.Dept = new Department(); c.Dept.Name = "Product"; c.Dept.DepartmentID = 1; c.Dept.DeptPersons = new Person[2]; Person p1=new Person(); p1.FirstName = "Andy"; p1.LastName = "Smith"; p1.Sex = "M"; p1.SecuritySN = "3101121103110110"; p1.Address = "Beijing,China"; p1.Email = "pro@chinacars.com"; Person p2 = new Person(); p2.FirstName = "Kate"; p2.LastName = "Allian"; p2.Sex = "F"; p2.SecuritySN = "3101110302011211"; p2.Address = "Beijing,China"; p2.Email = "pro@chinacars.com"; c.Dept[0] = p1; c.Dept[1] = p2; XmlSerializer xs = new XmlSerializer(typeof(Corp)); XmlTextWriter writer = null; try { writer = new XmlTextWriter(@"C:\Demo.XML", System.Text.Encoding.GetEncoding(0)); writer.Formatting = System.Xml.Formatting.Indented; xs.Serialize(writer, c); } finally { if (writer != null) writer.Close(); } }
让我们来看看结果:
生成的 XML 带有默认命名空间,有些人偏好 null 空间,因此您可以像下面这样更改代码。同样,如果您想更改 XML 的命名空间,只需更改命名空间参数即可。.NET XML 属性构造函数有许多参数,方便您更改为相应的 XML 属性。<? xml version="1.0" encoding="gb2312" > <Corp xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Name="ChinaCars"> <Department Name="Product" DepartmentID="1"> <Person> <FirstName>Andy</FirstName> <LastName>Smith</LastName> <Sex>M</Sex> <SecuritySN>3101121103110110</SecuritySN> <Address>Beijing,China</Address> <Email>pro@chinacars.com</Email> </Person> <Person> <FirstName>Kate</FirstName> <LastName>Allian</LastName> <Sex>F</Sex> <SecuritySN>3101110302011211</SecuritySN> <Address>Beijing,China</Address> <Email>pro@chinacars.com</Email> </Person> </Department> </Corp>
public static int WriteXML(String xmlFileName, Object oData,bool useNullNameSpace) { XmlSerializer xs = new XmlSerializer(oData.GetType()); XmlTextWriter writer = null; try { writer = new XmlTextWriter(xmlFileName, System.Text.Encoding.GetEncoding(0)); writer.Formatting = System.Xml.Formatting.Indented; if (useNullNameSpace) { XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); xs.Serialize(writer, oData,ns); } else { xs.Serialize(writer, oData); } writer.Close(); } finally { if (writer != null) { writer.Close(); } } return 0; }
现在,我们来反序列化它:
static void RunDemo2()
{
Corp c = null;
XmlSerializer xs = new XmlSerializer(typeof(Corp));
XmlTextReader reader = null;
try
{
reader = new XmlTextReader(@"C:\Demo.XML");
c = (Corp) xs.Deserialize(reader);
}
finally
{
if (reader != null) { reader.Close(); }
}
}
同样,这些代码可以重用,有些人可能偏好泛型而不是强制类型转换,请看下面的代码:
public static T ReadXML<T>(String xmlFilename) { return (T)ReadXML(xmlFilename, typeof(T)); } public static Object ReadXML(String xmlFilename, Type dataType) { Object objXML = null; XmlSerializer xs = new XmlSerializer(dataType); XmlTextReader reader = null; try { reader = new XmlTextReader(xmlFilename); objXML = xs.Deserialize(reader); } finally { if (reader != null) { reader.Close(); } } return objXML; }
第二部分:结合 XSLT 或转储部分 XML
有时人们可能想使用 xml-schema。XML-XSL 模式有很多优点。要做到这一点,您需要使用 XmlTextWriter 类的 WriteRaw 方法。
public static String GetXML( Object oData,String xslURL,bool useNullNameSpace) { XmlSerializer xs = new XmlSerializer(oData.GetType()); XmlTextWriter writer = null; System.IO.MemoryStream msStream = null; String strResult = null; try { msStream = new System.IO.MemoryStream(); //Use System's default encoding System.Text.Encoding defaultEnc = System.Text.Encoding.GetEncoding(0); writer = new XmlTextWriter(msStream, defaultEnc); writer.Formatting = System.Xml.Formatting.Indented; //Need the xslt file for display if (xslURL != null) { writer.WriteRaw("\r\n"); writer.WriteRaw(String.Format("{0}\r\n", xslURL)); } //nullspace processing if (useNullNameSpace) { XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); xs.Serialize(writer, oData, ns); } else { xs.Serialize(writer, oData); } writer.Close(); strResult = System.Text.Encoding.Default.GetString(msStream.ToArray()); } finally { if (msStream != null) { msStream.Close(); } if (writer != null) { writer.Close(); } } return strResult; }同样,您可以使用 WriteRaw 方法来编写任何您想写的内容。
我们可以都使用 xml 来转储,而不是为每个类编写 toString 方法,这对于调试特别有用。我们也可以将类的成员转储为 xml,我们所需要做的就是使用 XmlRootAttribute,这里是代码:
public static String GetShortXML(Object oData, String strType) { XmlRootAttribute xrAttr=new XmlRootAttribute(strType); XmlSerializer xs = new XmlSerializer(oData.GetType(),xrAttr); XmlTextWriter writer = null; System.IO.MemoryStream msStream = null; String strResult = null; try { msStream = new System.IO.MemoryStream(); writer = new XmlTextWriter(msStream, System.Text.Encoding.GetEncoding(0)); writer.Formatting = System.Xml.Formatting.None; writer.WriteRaw(null); XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); xs.Serialize(writer, oData, ns); writer.Close(); strResult = System.Text.Encoding.Default.GetString(msStream.ToArray()); } finally { if (msStream != null) { msStream.Close(); } if (writer != null) { writer.Close(); } } return strResult; }
第三部分:编辑 XML 文件
有时人们可能想编辑一个 XML 文件。首先,我们需要一个临时对象用于编辑,如果更改被接受,则将成员设置到临时对象,否则只需丢弃临时对象。问题是,如何克隆一个对象?逐字段复制是很好的。即使使用对象的 memberwiseClone 方法,我们仍然有很多事情要做。幸运的是,.NET 框架提供了一个通用的 SerializeClone 方法,可以进行通用的成员复制,它就像 Java 的序列化一样。我们所需要做的就是为每个类添加一个 .NET [Serializable] 属性,就像下面这个:
[Serializable] public class Corp { …… };
之后,您可以使用以下通用的 SerializeClone 方法来克隆一个副本:
public static T SerializeClone<T>(T srcObject) { System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bfFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); System.IO.MemoryStream msStream = new System.IO.MemoryStream(); T result = default(T); try { bfFormatter.Serialize(msStream, srcObject); msStream.Seek(0, System.IO.SeekOrigin.Begin); result=(T)bfFormatter.Deserialize(msStream); } finally { if (msStream != null) msStream.Close(); } return result; }
例如,我们可以通过这种方式复制一个 Corp 实例:
Corp t=SerializeClone(typeof(Corp))(destCorp);
让我们再来考虑编辑,我们所需要做的就是 AUD,即添加、更新和删除。在这里,添加意味着向数组添加新成员,删除意味着从数组中删除成员。借助反射和泛型,这个目标可以轻松实现。
public static T[] ArrayAppend<T>(T[] array, T newObject) { T[] newArray = ExtentArray<T>(array, 1); newArray[newArray.Length - 1] = newObject; return newArray; } public static T[] ArrayRemove<T>(T[] array, T destObject) { T[] newArray = ExtentArray<T>(array, -1); int nPos = Array.IndexOf(array, destObject); if (nPos >= 0) { for (int i = nPos; i < newArray.Length; i++) { newArray[i] = array[i + 1]; } } return newArray; } public static T[] ExtentArray<T>(T[] array, int nShrinkSize) { T[] result = null; if (array == null) { if (nShrinkSize >= 0) { result = new T[nShrinkSize]; for (int i = 0; i < nShrinkSize; i++) { result[i] =(T) typeof(T).GetConstructor(new Type[0]).Invoke(null); } } } else { result = new T[array.Length + nShrinkSize]; for (int i = 0; i < array.Length && i < result.Length; i++) { result[i] = array[i]; } for (int i = array.Length; i < result.Length; i++) { result[i] = (T)typeof(T).GetConstructor(new Type[0]).Invoke(null); } } return result; }
借助下面的 ArrayAppend 和 ArrayRemove 方法,可以轻松地向数组添加新成员或从数组中删除成员。可能会出现类似下面的代码:
Corp r=SerializeClone<Corp>(config); r.CorpName=newCorpName; …… ArrayAppend(r.Dept.DeptPersons,newPerson)
使用数组来表示 XML 数组的想法很直接,但是,我们可以使用另一种方法。
XML 序列化使用公共字段或属性。如果我们使用属性声明,我们可以获得一种更简单的方法。首先声明一个泛型列表,然后声明相应的 set 和 get 方法。 public class DepartmentU
{
private String _Name;
private int _DepartmentID;
private List<Person> _DeptPersonList=new List<Person>();
[XmlAttribute]
public String Name
{
get { return this._Name; }
set { this._Name = value; }
}
[XmlAttribute]
public int DepartmentID
{
get { return this._DepartmentID; }
set { this._DepartmentID = value; }
}
[XmlElement(ElementName = "Person")]
public Person[] DeptPersons
{
get { return this._DeptPersonList.ToArray(); }
set {
ChinaCars.Util.SysUtil.LoadListFromArray<Person>(_DeptPersonList, value);
}
}
public List<Person> GetDeptPersonList()
{
return _DeptPersonList;
}
public Person this[int i]
{
get { return DeptPersons[i]; }
set { DeptPersons[i] = value; }
}
};
这是一种简单的方法,您无需为类添加 Serializable 属性。通过 GetDeptPersonList 函数,您可以轻松地操作 Person 元素。这是处理 XML 数组元素的建议方法。
使用代码
通过使用 XMLUtil 类,您可以将对象序列化到文件,或序列化到字符串对象,反之亦然。
// Serialize a object to a file,if the useNullNameSpace parameter is true,then the object will be serialized with a empty namespace. public static int WriteXML(String xmlFileName, Object oData,bool useNullNameSpace) // If you want to read a xml from file and deserialize it to an object public static Object ReadXML(String xmlFilename, Type dataType) public static T ReadXML<T>(String xmlFilename) // If you just want to get the XML text of the object,then you can call the GetXML method. public static String GetXML(Object oData) // If you want to get the XML text of the object and you want a xsl description or you don't want a empty namespace public static String GetXML( Object oData,String xslURL,bool useNullNameSpace) // You may want to get part of the object as xml,you can do this public static String GetShortXML(Object oData, String strType) // You may want to deserialize a xml string or a byte array to an object public static Object LoadXML(String s, Type dataType) public static Object LoadXML(byte[] bRawData, Type dataType) public static Object LoadXML<T>(String s)
有些人可能想手动修改 XML,然后您可以使用 ArrayUtil 类来实现此目标,或者您可以利用文章中使用的技术。
// To find the elements matching the fieldname and value: public static T[] FindElementList<T>(T[] array, String strKeyWord, Object value) // To find the first element matching the fieldname and value public static T FindElement<T>(T[] array, String strKeyWord, Object value) // To append an element to the array: public static T[] ArrayAppend<T>(T[] array, T newObject) // To remove an element element from array public static T[] ArrayRemove<T>(T[] array, T destObject) //To extend or shirink the size of the array public static T[] ExtentArray<T>(T[] array, int nShrinkSize)
关注点
以一种“懒惰”的方式保存/加载/维护 XML。
源代码来自 SmartK Application Server,这是一个快速的 xslt 开发平台。
历史
2006 年 9 月 26 日 发布第一个版本