jump to navigation

XML Serializing between WebService proxy classes and Entity classes July 8, 2008

Posted by codinglifestyle in ASP.NET, C#.
Tags: , , , , ,
1 comment so far
If you are looking at this page I would suggest you ask yourself the following questions:
  • Is the architecture of my software broken?  Why am a referencing a class on either side of a webservice?
  • Are web services really necessary in this situation?
My answers are yes, it is broken, and yes, there is an egrigious use of web services throughout.  Unfortunately, this product is many years old and I have to do what the customer asks.  So here I have an entity class which is not a simple container for data.  It contains additional methods and logic.  This entity is populated by the database and then forked over by webservices.  However, the UI layer also needs access to the additional methods and logic in the entity.  The WSDL generated proxy class does not contain these methods or logic; just a simple representation of the public properties of the class with straight gets and sets.  
 
The challenge then, is to serialize the web service proxy class back to the entity class.  Once this is done, I will have access to the additional methods and logic I need.  We can do this by using what web services use, XML serialization.  Its a fast way of getting our data from the proxy class back to XML.  Then deserialize the XML back to the entity class.
 
        ///
        /// Serialize the data from an object of Type_A to a new object of Type_B
        ///
        ///The Type_A object containing the data to serialize
        ///The Type_B to be created and deserialized with the Type_A object
        ///An object of Type_B
        static object SerializeObjects(object oSource, Type tDestination)
        {
            XmlNoNamespaceWriter xw = null;
            Object oDestination = null;
 
            try
            {
                //Prepare to convert XML entity to XmlNoNamespaceWriter
                MemoryStream ms = new MemoryStream();
                xw = new XmlNoNamespaceWriter(ms, Encoding.UTF8);
                XmlSerializer xsA = new XmlSerializer((oSource.GetType()));
 
                //Serialize web service xml data to xml stream
                xsA.Serialize(xw, oSource);
 
                //Deserialize stream to entity actual
                XmlSerializer xsB = new XmlSerializer(tDestination);
                xw.BaseStream.Position = 0;
                oDestination = (WSEntityTest.Entity)xsB.Deserialize(xw.BaseStream);
            }
            catch
            { }
            finally
            {
                //Close the stream
                if (xw != null)
                    xw.Close();
            }
 
            return oDestination;
        }
 
So what we are doing is using a memory stream and XmlTextWriter to contain the XML.  The XmlSerializer serializes the proxy class to the stream.  If you wanted to look at the resulting XML, you could use an XmlDocument to load the stream and take a look.  If we do that, we will notice a small problem.  Every child element will contain a namespace attribute (xmlns). This will throw a spanner in the works when deserializing the data to our entity class. Basically what the namespace is telling the XmlSerializer is the data doesn’t belong in our entity class so it will skip it. It’s just doing its job because technically we shouldn’t be serializing data from the class of type A to a class of type B. However, this is exactly what we need to do. To work around this, I looked high and low for a simple way of turning off the namespace (in .NET v2.0).  Nothing simple turned up, hence the use of XmlNoNamespaceWriter which is derived from XmlTextWriter.   The implementation of this class is shown below:
 
    public class XmlNoNamespaceWriter : System.Xml.XmlTextWriter
    {
        bool m_bSkipAttribute = true;
 
        public XmlNoNamespaceWriter(System.IO.Stream writer, System.Text.Encoding encoding) : base(writer, encoding)
        {
        }
 
        public override void WriteStartElement(string sPrefix, string sLocalName, string sNS)
        {
            base.WriteStartElement(null, sLocalName, null);
        }
 
 
        public override void WriteStartAttribute(string sPrefix, string sLocalName, string sNS)
        {
            //If the sPrefix or localname are “xmlns”, don’t write it.
            if (sPrefix.CompareTo(“xmlns”) == 0 || sLocalName.CompareTo(“xmlns”)==0)
            {
                m_bSkipAttribute = true;               
            }
            else
            {
                base.WriteStartAttribute(null, sLocalName, null);
            }
        }
 
        public override void WriteString(string sText)
        {
            //If we are writing an attribute, the sText for the xmlns
            //or xmlns:sPrefix declaration would occur here. Skip
            //it if this is the case.
            if(!m_bSkipAttribute)
            {
                base.WriteString(sText);
            }
        }
 
        public override void WriteEndAttribute()
        {
            //If we skipped the WriteStartAttribute call, we have to
            //skip the WriteEndAttribute call as well or else the XmlWriter
            //will have an invalid state.
            if(!m_bSkipAttribute)
            {
                base.WriteEndAttribute();
            }
            //reset the boolean for the next attribute.
            m_bSkipAttribute = false;
        }
 
 
        public override void WriteQualifiedName(string sLocalName, string sNS)
        {
            //Always write the qualified name using only the
            //localname.
            base.WriteQualifiedName(sLocalName,null);
        }
    }
 
I found this class here, and modified it slightly to use a stream rather than a file. So now we get past our namespace issue and the serializer won’t be confused by the namespaces. Now all we need is a second XmlSerializer created with our entity type.  This will deserialize the stream in to a new instance of our entity class.  The usage of the method is shown below:
 
//Retreive the WebService proxy class
localhost.Service1 svc       = new Client.localhost.Service1();
localhost.Entity xmlEntity   = svc.HelloWorld(); 
 
//Serialize the proxy class to a new entity class
Entity entity = (Entity) Program.SerializeObjects(xmlEntity, typeof(Entity));
 

Using reflection to copy a DataRow to a class September 14, 2007

Posted by codinglifestyle in ASP.NET, C#.
Tags: , , , ,
add a comment

Very often when working with DB tables we serialize a row in to class.  This entity class simply holds the data and hosts a rack of get and set methods.  The data layer serializes this class, the business logic layer manipulated it, and the UI layer ultimately displays it.  Here is a simple example:

 

    class Class1

    {

        private string m_sProp1;

        private int m_nProp2;

 

        public string Prop1

        {

            get

            {

                return m_sProp1;

            }

            set

            {

                m_sProp1 = value;

            }

        }

 

        public int Prop2

        {

            get

            {

                return m_nProp2;

            }

            set

            {

                m_nProp2 = value;

            }

        }

    }

 

Very often somewhere in the UI and/or logic layers you have a large function laboriously copying each value in or out of the entity class.  In my case, I was serializing the entity to a DataSet to be displayed in a Repeater control.  So I thought to myself, “Self, wouldn’t it be cool to use reflection to automatically perform the copy”.  While I didn’t know much about reflection a couple hours ago, it’s remarkably easy to use and, as you probably know, very powerful.  Let’s get started…

 

I need a copy function to copy a DataRow to my entity class and vice versa.  We’ll address the first case. 

 

        public static object SetValuesToObject(Type tClass, DataRow dr)

        {

            try

            {

                Object oClass        = System.Activator.CreateInstance(tClass);

                MemberInfo[] methods = tClass.GetMethods();

                ArrayList aMethods   = new ArrayList();

                object[] aoParam     = new object[1];

 

                //Get simple SET methods

                foreach (MethodInfo method in methods)

                {

                    if (method.DeclaringType == tClass && method.Name.StartsWith(“set_”))

                        aMethods.Add(method);

                }

 

                //Invoke each set method with mapped value

                for (int i = 0; i < aMethods.Count; i++)

                {

                    try

                    {

                        MethodInfo mInvoke = (MethodInfo)aMethods[i];

                        //Remove “set_” from method name

                        string sColumn = mInvoke.Name.Remove(0, 4);

                        //If row contains value for method…

                        if (dr.Table.Columns.Contains(sColumn))

                        {

                            //Get the parameter (always one for a set property)

                            ParameterInfo[] api = mInvoke.GetParameters();

                            ParameterInfo   pi  = api[0]; 

 

                            //Convert value to parameter type

                            aoParam[0] = Convert.ChangeType(dr[sColumn], pi.ParameterType);

                           

                            //Invoke the method

                            mInvoke.Invoke(oClass, aoParam);

                        }

                    }

                    catch

                    {

                        System.Diagnostics.Debug.Assert(false, “SetValuesToObject failed to set a value to an object”);

                    }

                }

                return oClass;

            }

            catch

            {

                System.Diagnostics.Debug.Assert(false, “SetValuesToObject failed to create an object”);

                return null;

            }

        } 



The way this works is I’m going to pass in a Type and a DataRow.  First we look through at all the methods in the entity class and pull out all the set methods.  Then look at the DataRow to see if we have a column which corresponds to the method.  Looking at the example entity class above, I would be hoping to have a column called Prop1 and Prop2*.  We the look to see what type of set method is expecting: string, int, DateTime, ect.  We use this to convert the value from the DataRow and set this in to an object array of length 1.  Finally, invoke the set method passing in the object array.  The end result, a newly instantiated entity class pre-populated with the values from the DataRow.

 

* Although it’s rare to abide by a naming convention in reality, this is new code, so we will keep naming identical from DB to entity to DataTable.  It would be trivial to introduce a mapping of column to property.