[Mono-list] patch: XML Deserialization

Elan Feingold efeingold@mn.rr.com
Thu, 27 Feb 2003 13:44:34 -0600


This is a multi-part message in MIME format.

------=_NextPart_000_00BD_01C2DE66.5D66BDC0
Content-Type: text/plain;
	charset="us-ascii"
Content-Transfer-Encoding: 7bit

* XmlCustomFormatter.cs: Support for converting byte arrays to and from
Base64.

* XmlSerialize.cs: Fix serialization of arrays to work better.
Implemented simple XML deserialization, works for nested objects,
arrays, etc. Most complex thing I tested was a structure containing two
arrays of structures. The code is fairly simple and easy to extend.

My first C# and first Mono work, so please don't laugh.

Best regards,

-elan

------=_NextPart_000_00BD_01C2DE66.5D66BDC0
Content-Type: application/octet-stream;
	name="patch"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="patch"

Index: class/System.XML/System.Xml.Serialization/XmlCustomFormatter.cs=0A=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0A=
RCS file: =
/mono/mcs/class/System.XML/System.Xml.Serialization/XmlCustomFormatter.cs=
,v=0A=
retrieving revision 1.4=0A=
diff -u -b -r1.4 XmlCustomFormatter.cs=0A=
--- class/System.XML/System.Xml.Serialization/XmlCustomFormatter.cs	19 =
Sep 2002 07:16:01 -0000	1.4=0A=
+++ class/System.XML/System.Xml.Serialization/XmlCustomFormatter.cs	27 =
Feb 2003 19:40:56 -0000=0A=
@@ -16,10 +16,9 @@=0A=
 =0A=
 		#region Methods=0A=
 =0A=
-		[MonoTODO]=0A=
-		internal static byte[] FromByteArrayBase64 (byte[] value)=0A=
+		internal static string FromByteArrayBase64 (byte[] value)=0A=
 		{=0A=
-			throw new NotImplementedException ();=0A=
+            return Convert.ToBase64String(value);=0A=
 		}=0A=
 =0A=
 		internal static string FromByteArrayHex (byte[] value)=0A=
@@ -86,6 +85,11 @@=0A=
 			foreach (string token in tokens)=0A=
 				output.Append (FromXmlNmToken (token));=0A=
 			return output.ToString ();=0A=
+		}=0A=
+=0A=
+		internal static byte[] ToByteArrayBase64 (string value)=0A=
+		{=0A=
+            return Convert.FromBase64String(value);=0A=
 		}=0A=
 =0A=
 		internal static char ToChar (string value)=0A=
Index: =
class/System.XML/System.Xml.Serialization/XmlSerializationWriter.cs=0A=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0A=
RCS file: =
/mono/mcs/class/System.XML/System.Xml.Serialization/XmlSerializationWrite=
r.cs,v=0A=
retrieving revision 1.6=0A=
diff -u -b -r1.6 XmlSerializationWriter.cs=0A=
--- class/System.XML/System.Xml.Serialization/XmlSerializationWriter.cs	=
13 Sep 2002 21:15:11 -0000	1.6=0A=
+++ class/System.XML/System.Xml.Serialization/XmlSerializationWriter.cs	=
27 Feb 2003 19:40:56 -0000=0A=
@@ -84,7 +84,7 @@=0A=
 			return new InvalidOperationException (message);=0A=
 		}=0A=
 =0A=
-		protected static byte[] FromByteArrayBase64 (byte[] value)=0A=
+		protected static string FromByteArrayBase64 (byte[] value)=0A=
 		{=0A=
 			return XmlCustomFormatter.FromByteArrayBase64 (value);=0A=
 		}=0A=
Index: class/System.XML/System.Xml.Serialization/XmlSerializer.cs=0A=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=0A=
RCS file: =
/mono/mcs/class/System.XML/System.Xml.Serialization/XmlSerializer.cs,v=0A=
retrieving revision 1.18=0A=
diff -u -b -r1.18 XmlSerializer.cs=0A=
--- class/System.XML/System.Xml.Serialization/XmlSerializer.cs	16 Feb =
2003 07:27:47 -0000	1.18=0A=
+++ class/System.XML/System.Xml.Serialization/XmlSerializer.cs	27 Feb =
2003 19:40:57 -0000=0A=
@@ -17,6 +17,7 @@=0A=
 using System.Reflection;=0A=
 using System.Xml;=0A=
 using System.Xml.Schema;=0A=
+using System.Text;=0A=
 =0A=
 namespace System.Xml.Serialization {	=0A=
 	/// <summary>=0A=
@@ -162,20 +163,206 @@=0A=
 			throw new NotImplementedException ();=0A=
 		}=0A=
 =0A=
-		[MonoTODO]=0A=
 		public object Deserialize (Stream stream)=0A=
 		{=0A=
-			throw new NotImplementedException ();=0A=
+            XmlTextReader xmlReader =3D new XmlTextReader(stream);=0A=
+			return Deserialize(xmlReader);=0A=
 		}=0A=
-		[MonoTODO]=0A=
+=0A=
 		public object Deserialize (TextReader textReader)=0A=
 		{=0A=
-			throw new NotImplementedException ();=0A=
+			XmlTextReader xmlReader =3D new XmlTextReader(textReader);=0A=
+            return Deserialize(xmlReader);=0A=
 		}=0A=
-		[MonoTODO]=0A=
-		public object Deserialize (XmlReader xmlReader)=0A=
+=0A=
+        public bool DeserializeComposite(XmlReader xmlReader, ref =
Object theObject)=0A=
 		{=0A=
-			throw new NotImplementedException ();=0A=
+            Type objType =3D theObject.GetType();=0A=
+            bool retVal  =3D true;=0A=
+=0A=
+            //Console.WriteLine("DeserializeComposite({0})", objType);=0A=
+            =0A=
+            // Are we at an empty element?=0A=
+            if (xmlReader.IsEmptyElement =3D=3D true)=0A=
+                return retVal;=0A=
+=0A=
+            // Read each field, counting how many we find.=0A=
+            for (int numFields=3D0; xmlReader.Read(); )=0A=
+            {=0A=
+                XmlNodeType xmlNodeType =3D xmlReader.NodeType;=0A=
+                bool        isEmpty     =3D xmlReader.IsEmptyElement;=0A=
+                =0A=
+                if (xmlNodeType =3D=3D XmlNodeType.Element)=0A=
+                {=0A=
+                    // Read the field.=0A=
+                    DeserializeField(xmlReader, ref theObject, =
xmlReader.Name);=0A=
+                    numFields++;=0A=
+                }=0A=
+                else if (xmlNodeType =3D=3D XmlNodeType.EndElement)=0A=
+                {=0A=
+                    if (numFields =3D=3D 0)=0A=
+                    {=0A=
+                        //Console.WriteLine("Empty object deserialized, =
ignoring.");=0A=
+                        retVal =3D false;=0A=
+                    }=0A=
+                    =0A=
+                    return retVal;=0A=
+                }=0A=
+            }=0A=
+=0A=
+            return retVal;=0A=
+        }=0A=
+=0A=
+        public void DeserializeField(XmlReader  xmlReader,=0A=
+                                     ref Object theObject,=0A=
+                                     String     fieldName)=0A=
+        {=0A=
+            // Get the type=0A=
+            FieldInfo fieldInfo    =3D =
theObject.GetType().GetField(fieldName);=0A=
+            Type      fieldType    =3D fieldInfo.FieldType;=0A=
+            Object    value        =3D null;=0A=
+            bool      isEmptyField =3D xmlReader.IsEmptyElement;=0A=
+=0A=
+            //Console.WriteLine("DeserializeField({0} of type {1}", =
fieldName, fieldType);=0A=
+=0A=
+            if (fieldType.IsArray && fieldType !=3D =
typeof(System.Byte[]))=0A=
+            {=0A=
+                // Create an empty array list.=0A=
+                ArrayList list =3D new ArrayList();=0A=
+=0A=
+                // Call out to deserialize it.=0A=
+                DeserializeArray(xmlReader, list, =
fieldType.GetElementType());=0A=
+                value =3D list.ToArray(fieldType.GetElementType());=0A=
+            }=0A=
+            else if (isEmptyField =3D=3D true && fieldType.IsArray)=0A=
+            {=0A=
+                // Must be a byte array, just create an empty one.=0A=
+                value =3D new byte[0];=0A=
+            }=0A=
+            else if (isEmptyField =3D=3D false && =0A=
+                     (IsInbuiltType(fieldType) || fieldType.IsEnum || =
fieldType.IsArray))=0A=
+            {=0A=
+                // Built in, set it.=0A=
+                while (xmlReader.Read())=0A=
+                {=0A=
+                    if (xmlReader.NodeType =3D=3D XmlNodeType.Text)=0A=
+                    {=0A=
+                        //Console.WriteLine(" -> value is '{0}'", =
xmlReader.Value);=0A=
+                        =0A=
+                        if (fieldType =3D=3D typeof(Guid))=0A=
+                            value =3D =
XmlConvert.ToGuid(xmlReader.Value);=0A=
+                        else if (fieldType =3D=3D typeof(Boolean))=0A=
+                            value =3D =
XmlConvert.ToBoolean(xmlReader.Value);=0A=
+                        else if (fieldType =3D=3D typeof(String))=0A=
+                            value =3D xmlReader.Value;=0A=
+                        else if (fieldType =3D=3D typeof(Int64))=0A=
+                            value =3D =
XmlConvert.ToInt64(xmlReader.Value);=0A=
+                        else if (fieldType =3D=3D typeof(DateTime))=0A=
+                            value =3D =
XmlConvert.ToDateTime(xmlReader.Value);=0A=
+                        else if (fieldType.IsEnum)=0A=
+                            value =3D Enum.Parse(fieldType, =
xmlReader.Value);=0A=
+                        else if (fieldType =3D=3D typeof(System.Byte[]))=0A=
+                            value =3D =
XmlCustomFormatter.ToByteArrayBase64(xmlReader.Value);=0A=
+                        else=0A=
+                            Console.WriteLine("Error (type is '{0})'", =
fieldType);=0A=
+                        =0A=
+                        break;=0A=
+                    }=0A=
+                }=0A=
+            }=0A=
+            else=0A=
+            {=0A=
+                //Console.WriteLine("Creating new {0}", fieldType);=0A=
+=0A=
+                // Create the new complex object.=0A=
+                value =3D System.Activator.CreateInstance(fieldType);=0A=
+=0A=
+                // Recurse, allowing the method to whack the object if =
it's empty.=0A=
+                DeserializeComposite(xmlReader, ref value);=0A=
+            }=0A=
+=0A=
+            //Console.WriteLine(" Setting {0} to '{1}'", fieldName, =
value);=0A=
+=0A=
+            // Set the field value.=0A=
+            theObject.GetType().InvokeMember(fieldName,=0A=
+                                             BindingFlags.SetField, =0A=
+                                             null,=0A=
+                                             theObject, =0A=
+                                             new Object[] { value },=0A=
+                                             null, null, null);=0A=
+=0A=
+            // We need to munch the end.=0A=
+            if (IsInbuiltType(fieldType) || =0A=
+                fieldType.IsEnum         || =0A=
+                fieldType =3D=3D typeof(System.Byte[]))=0A=
+            {=0A=
+                if (isEmptyField =3D=3D false)=0A=
+                    while (xmlReader.Read() && xmlReader.NodeType !=3D =
XmlNodeType.EndElement)=0A=
+                        ;=0A=
+            }=0A=
+            =0A=
+        }=0A=
+=0A=
+        public void DeserializeArray(XmlReader xmlReader, ArrayList =
theList, Type theType)=0A=
+        {=0A=
+            //Console.WriteLine(" DeserializeArray({0})", theType);=0A=
+=0A=
+            if (xmlReader.IsEmptyElement)=0A=
+            {=0A=
+                //Console.WriteLine("  DeserializeArray -> empty, =
nothing to do here");=0A=
+                return;=0A=
+            }=0A=
+=0A=
+            while (xmlReader.Read())=0A=
+            {=0A=
+                XmlNodeType xmlNodeType =3D xmlReader.NodeType;=0A=
+                bool        isEmpty     =3D xmlReader.IsEmptyElement;=0A=
+=0A=
+                if (xmlNodeType =3D=3D XmlNodeType.Element)=0A=
+                {=0A=
+                    // Must be an element of the array, create it.=0A=
+                    Object obj =3D =
System.Activator.CreateInstance(theType);=0A=
+=0A=
+                    //Console.WriteLine("  created obj of type '{0}'", =
obj.GetType());=0A=
+=0A=
+                    // Deserialize and add.=0A=
+                    if (DeserializeComposite(xmlReader, ref obj))=0A=
+                    {=0A=
+                        theList.Add(obj);=0A=
+                    }=0A=
+                }=0A=
+                =0A=
+                if ((xmlNodeType =3D=3D XmlNodeType.Element && isEmpty) =
||=0A=
+                    (xmlNodeType =3D=3D XmlNodeType.EndElement))=0A=
+                {=0A=
+                    return;=0A=
+                }=0A=
+            }=0A=
+        }=0A=
+=0A=
+		public object Deserialize(XmlReader xmlReader)=0A=
+		{=0A=
+            Object obj =3D null;=0A=
+=0A=
+            // Read each node in the tree.=0A=
+            while (xmlReader.Read())=0A=
+            {=0A=
+                if (xmlReader.NodeType =3D=3D XmlNodeType.Element)=0A=
+                {=0A=
+                    // Create the top level object.=0A=
+                    //Console.WriteLine("Creating '{0}'", =
xsertype.FullName);=0A=
+                    obj =3D System.Activator.CreateInstance(xsertype);=0A=
+=0A=
+                    // Deserialize it.=0A=
+                    DeserializeComposite(xmlReader, ref obj);=0A=
+                }=0A=
+                else if (xmlReader.NodeType =3D=3D =
XmlNodeType.EndElement)=0A=
+                {=0A=
+                    return obj;=0A=
+                }=0A=
+            }                  =0A=
+=0A=
+            return obj;=0A=
 		}=0A=
 =0A=
 		protected virtual object Deserialize (XmlSerializationReader reader)=0A=
@@ -477,9 +664,9 @@=0A=
 					elementType  =3D propertyInfo.PropertyType;=0A=
 					elementValue =3D propertyInfo.GetValue (o, null);=0A=
 				}=0A=
-=0A=
 				elementName =3D xmlElements.GetElementName (elementType, =
member.Name);=0A=
 				elementNs =3D xmlElements.GetElementNamespace (elementType);=0A=
+=0A=
 				WriteElement (writer, xmlElements, elementName, elementNs, =
elementType, elementValue);=0A=
 			}=0A=
 		}=0A=
@@ -558,7 +745,7 @@=0A=
 			}=0A=
 			else if (type.IsEnum)=20
 			{=0A=
-				// FIXME=0A=
+                writer.WriteString(GetXmlValue(value));=0A=
 			}=0A=
 			else
 			{ //Complex Type=0A=
@@ -581,9 +768,8 @@=0A=
 			string arrayTypeName =3D =
TypeTranslator.GetTypeData(arrayType).ElementName;=0A=
 			=0A=
 			TypeData td =3D TypeTranslator.GetTypeData (arrayType);=0A=
-			writer.WriteStartElement (td.ElementName);=0A=
-			Console.WriteLine(td.ElementName);=0A=
-			//Special Treatment for Byte array=0A=
+=0A=
+			// Special Treatment for Byte array=0A=
 			if(arrayType.Equals(typeof(byte)))=0A=
 			{=0A=
 				WriteBuiltinValue(writer,o);=0A=
@@ -592,6 +778,8 @@=0A=
 			{=0A=
 				for(int i=3D0; i< arr.Length; i++)=0A=
 				{=0A=
+                    writer.WriteStartElement (td.ElementName);=0A=
+=0A=
 					object value =3D arr.GetValue(i);=0A=
 					if (IsInbuiltType (arrayType))=20
 					{=0A=
@@ -601,9 +789,10 @@=0A=
 					{=0A=
 						SerializeMembers(writer, value, false);=0A=
 					}=0A=
+=0A=
+                    writer.WriteEndElement();=0A=
 				}=0A=
 			}=0A=
-			writer.WriteEndElement();=0A=
 		}=0A=
 =0A=
 		/// <summary>=0A=
@@ -615,6 +804,7 @@=0A=
 		[MonoTODO ("Remove FIXMEs")]=0A=
 		private void FillTypeTable (Type type)=0A=
 		{=0A=
+            // If it's already in the table, don't add it again.=0A=
 			if (typeTable.Contains (type))=0A=
 				return;=0A=
 =0A=
@@ -634,8 +824,8 @@=0A=
 			}=0A=
 			else {=0A=
 				//There must be a public constructor=0A=
-				if (!HasDefaultConstructor (type))=0A=
-					throw new Exception ("Can't Serialize Type " + type.Name + " since =
it does not have default Constructor");=0A=
+				//if (!HasDefaultConstructor (type))=0A=
+                //throw new Exception ("Can't Serialize Type " + =
type.Name + " since it does not have default Constructor");=0A=
 =0A=
 				if (type.GetInterface ("ICollection") =3D=3D typeof =
(System.Collections.ICollection)) {=0A=
 					FillICollectionType (type);=0A=
@@ -681,6 +871,7 @@=0A=
 					continue;=0A=
 =0A=
 				if (fieldInfo !=3D null) {=0A=
+=0A=
 					//If field is readOnly or const, do not serialize it.=0A=
 					if (fieldInfo.IsLiteral || fieldInfo.IsInitOnly)=0A=
 						continue;=0A=
@@ -724,11 +915,15 @@=0A=
 						FillTypeTable (fieldInfo.FieldType);=0A=
 				} =0A=
 				else if (propertyInfo !=3D null) {=0A=
+=0A=
 					//If property is readonly or writeonly, do not serialize it.=0A=
 					//Exceptions are properties whose return type is array, =
ICollection or IEnumerable=0A=
 					//Indexers are not serialized unless the class Implements =
ICollection.=0A=
-					if (!(propertyInfo.PropertyType.IsArray || Implements =
(propertyInfo.PropertyType, typeof (ICollection)) || =0A=
-						(propertyInfo.PropertyType !=3D typeof (string) && Implements =
(propertyInfo.PropertyType, typeof (IEnumerable))))) {=0A=
+					if (!(propertyInfo.PropertyType.IsArray || =0A=
+                          Implements (propertyInfo.PropertyType, typeof =
(ICollection)) || =0A=
+                          (propertyInfo.PropertyType !=3D typeof =
(string) && =0A=
+                           Implements (propertyInfo.PropertyType, =
typeof (IEnumerable)))))=0A=
+                    {=0A=
 						if(!(propertyInfo.CanRead && propertyInfo.CanWrite) || =
propertyInfo.GetIndexParameters ().Length !=3D 0)=0A=
 							continue;=0A=
 					}=0A=
@@ -858,9 +1053,12 @@=0A=
 		{=0A=
 			if (type.IsEnum)=0A=
 				return false;=0A=
-			if (type.IsValueType || type =3D=3D typeof (string) || =
type.IsPrimitive)=0A=
+			if (/* type.IsValueType || */type =3D=3D typeof (string) || =
type.IsPrimitive)=0A=
 				return true;=0A=
-			if (type =3D=3D typeof (DateTime) || type =3D=3D typeof (XmlNode) || =
type.IsSubclassOf (typeof (XmlNode)))=0A=
+			if (type =3D=3D typeof (DateTime) || =0A=
+                type =3D=3D typeof (Guid)     ||=0A=
+                type =3D=3D typeof (XmlNode)  || =0A=
+                type.IsSubclassOf (typeof (XmlNode)))=0A=
 				return true;=0A=
 				=0A=
 			return false;=0A=
@@ -931,7 +1129,7 @@=0A=
 			}=0A=
 			#endregion=0A=
 			if (value is byte[])=0A=
-				return XmlCustomFormatter.FromByteArrayHex((byte[])value);=0A=
+                return =
XmlCustomFormatter.FromByteArrayBase64((byte[])value);=0A=
 			if (value is Guid)=0A=
 				return XmlConvert.ToString((Guid)value);=0A=
 			if(value is DateTime)=0A=

------=_NextPart_000_00BD_01C2DE66.5D66BDC0--