[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--