Introduction
The XML standard now has become widely used nowadays.However,there're still many people use XML API to deal with XML.This is very trivial and error-prone.This article shows how to intrigrate XML function in new project or migrate an old project smoothly.
Part I. Reading And Wring a XML
.NET introduce a class named XmlSerializer which can serialize a object to xml and vice versa.Let's measure about a XML document.It must contain a root element,which maps to the XmlRoot attribute in .NET.A XML document may contain element,attribute,text etc.In turn they maps to XmlElement,XmlAttribute,XmlText class in .NET framework.
Look at the following graph,we use it for the demo:
First let's implement the serialization Corp class.We need to create a instance of XmlSerializer,then use a XmlTextWriter to serialize it.
We define the struct in datadef.cs,here below is the content:
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; }
}
};
}
When .NET process the serialization,it will process the public access member only,either field or property is OK.If you do not want the member serialized,add a XmlIgnore .NET attribute.By default,the serialization will treat the member as element ,so if you wanma add a attribute, you should add a XmlAttribute .NET attribute.Also,the serialization take the member name as the element name or attribute name,so if you want to change the name,you should change the name in the .NET attribute declaration as the above demo. And this also means that,if you add or remove some elements,you don't need to change any code ,this is a advantage compare to traditional method.The most interesting thing (see red underline code) is that you can also delcare the array as a element directly.
Here is the demo code that serialize Corp class:
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();
}
}
Let's measure the result:
"left"><? 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>
The XML comes out with a default namespace,some people prefer a null space,so you can change the code as below.Also,if you want to change the namespace of the xml,just change the namespace parameter.All of .NET XML Attribute constructor have many parameters which is convient for you to change to corresponding xml attribute.
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;
}
Now,let's deserialize it:
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(); }
}
}
Also,these code can be reused,some people may prefer generic type than cast,see the flowing:
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;
}
Part II. Combine With XSLT Or Dump part XML
Some times people may want use xml-schema.The XML-XSL schema has a lot of advantange.To do so,you need to use the WriteRaw method of the XmlTextWriter class:
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();
System.Text.Encoding defaultEnc = System.Text.Encoding.GetEncoding(0);
writer = new XmlTextWriter(msStream, defaultEnc);
writer.Formatting = System.Xml.Formatting.Indented;
if (xslURL != null)
{
writer.WriteRaw("\r\n");
writer.WriteRaw(String.Format("{0}\r\n", xslURL));
}
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;
}
Also,you can use the WriteRaw menthod to write anything you want to write.
We can all use xml for dump instead of write a toString method for every class,this is expecailly useful for debuging.Also we can also dump member of a class as a xml,all we need to do is using the XmlRootAttribute, here is the code:
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;
}
Part III. Editing a XML File
There're chances that people want to edit a xml file.First,we need a temp object for editing,if the changes are accept,then set the member to the temp object otherwise just discard the temp object.The question is,how to clone a object?Copy fields by fields is awsome.Even with the memberwiseClone menthd of a object,we'all still have a lot of things to do.Fortunatelly,the .NET framework offer a generic SerializeClone method which can do a generic member copy,it just like like Java's serialize.All we need to do is add a .NET [Serializable] for every class,just like the one below:
[Serializable]
public class Corp
{
��
};
After that,you can use the following generic SerializeClone method to clone a copy:
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;
}
For example,we can copy a Corp instance in this way:
Corp t=SerializeClone(typeof(Corp))(destCorp);
Let's think about the editing again,all we need to to is AUD,that's add,update and delete.Here,the add means add a new member to a array,and delete means remove a member from a array.With the help of reflection and generic,this target can simply achieved.
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;
}
With the help of ArrayAppend and ArrayRemove method below,it's now easy to either add a new member to or remove a member from a array.There may be a code like below:
Corp r=SerializeClone<Corp>(config);
r.CorpName=newCorpName;
��
ArrayAppend(r.Dept.DeptPersons,newPerson)
The idea of using array for xml array is rather straight,howerver,we can use another method.
The xml serialization is using public fields or properties.If we use properties declaration,we can have a much more easy way.First we declare a generic list,then we declare the corresponding set & get method.
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; }
}
};
This is an easy way and you don't need add a Serializable attribute to the class.Through the GetDeptPersonList function,you can manipulate the Person elements easily.And this is the suggest way for manipulate the XML array elements.
Using the code
By using the XMLUtil class,you can serialize a object to a file,or to a string object and vice versa.
public static int WriteXML(String xmlFileName, Object oData,bool useNullNameSpace)
public static Object ReadXML(String xmlFilename, Type dataType)
public static T ReadXML<T>(String xmlFilename)
public static String GetXML(Object oData)
public static String GetXML( Object oData,String xslURL,bool useNullNameSpace)
public static String GetShortXML(Object oData, String strType)
public static Object LoadXML(String s, Type dataType)
public static Object LoadXML(byte[] bRawData, Type dataType)
public static Object LoadXML<T>(String s)
Some people may to to modify the XML manually,then you can use the ArrayUtil class to archive this target or you can utilize the technique use in the article above:
public static T[] FindElementList<T>(T[] array, String strKeyWord, Object value)
public static T FindElement<T>(T[] array, String strKeyWord, Object value)
public static T[] ArrayAppend<T>(T[] array, T newObject)
public static T[] ArrayRemove<T>(T[] array, T destObject)
public static T[] ExtentArray<T>(T[] array, int nShrinkSize)
Points of Interest
Save/Load/Maintenace the XML in a lazy way.
The source comes from SmartK Application Server,which is a fast xslt development platform.
History
Sep 26th,2006 Publish the first version