Click here to Skip to main content
Click here to Skip to main content

Accelerate Your XML Development Under .NET 2.0

, 8 Mar 2007
Rate this:
Please Sign up or sign in to vote.
An article on using XML in a lazy way

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:

<p align="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>









</p>
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();
              //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;
        }
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.

// 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)        
    

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:

// 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)

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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Jinjun Xie
Web Developer
China China
About the author:
Jinjin Xie is the technical director of ChinaCars Co.LTD, expertise in Application Architect,Performance Tunning and VLDB design.
email:jinjun@jinjun.com.
office phone:8610-64014646 ext. 779

Comments and Discussions

 
Generalnice artical Pinmemberbraveheal9-Apr-08 21:44 
QuestionWhat a beautiful code PinmemberAghaKhan25-Apr-07 13:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140814.1 | Last Updated 8 Mar 2007
Article Copyright 2007 by Jinjun Xie
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid