// // Mono Class Libraries // System.Xml.Serialization.XmlSerializer // // Authors: // John Donagher (john@webmeta.com) // Ajay kumar Dwivedi (adwiv@yahoo.com) // Tim Coleman (tim@timcoleman.com) // Elan Feingold (ef10@cornell.edu) // // (C) 2002 John Donagher, Ajay kumar Dwivedi // Copyright (C) Tim Coleman, 2002 // (C) 2003 Elan Feingold // using System; using System.Collections; using System.IO; using System.Reflection; using System.Xml; using System.Xml.Schema; using System.Text; namespace System.Xml.Serialization { /// /// Summary description for XmlSerializer. /// public class XmlSerializer { #region Fields Type xsertype; XmlAttributeOverrides overrides; Type[] extraTypes; XmlRootAttribute rootAttribute; string defaultNamespace; Hashtable typeTable; bool useOrder; bool isNullable; Hashtable typeMappings = new Hashtable (); #endregion // Fields #region Constructors protected XmlSerializer () { } public XmlSerializer (Type type) : this (type, null, null, null, null) { } public XmlSerializer (XmlTypeMapping xmlTypeMapping) { typeMappings.Add (xmlTypeMapping.TypeFullName, xmlTypeMapping); } public XmlSerializer (Type type, string defaultNamespace) : this (type, null, null, null, defaultNamespace) { } public XmlSerializer (Type type, Type[] extraTypes) : this (type, null, extraTypes, null, null) { } public XmlSerializer (Type type, XmlAttributeOverrides overrides) : this (type, overrides, null, null, null) { } public XmlSerializer (Type type, XmlRootAttribute root) : this (type, null, null, root, null) { } internal XmlSerializer (Hashtable typeTable) { this.typeTable = typeTable; } public XmlSerializer (Type type, XmlAttributeOverrides overrides, Type [] extraTypes, XmlRootAttribute root, string defaultNamespace) { if (type == null) throw new ArgumentNullException ("type"); XmlReflectionImporter ri = new XmlReflectionImporter (overrides, defaultNamespace); TypeData td = TypeTranslator.GetTypeData (type); typeMappings.Add (td.FullTypeName, ri.ImportTypeMapping (type, root, defaultNamespace)); ri.IncludeTypes (type); if (extraTypes != null) { foreach (Type t in extraTypes) { td = TypeTranslator.GetTypeData (t); string n = td.FullTypeName; typeMappings.Add (n, ri.ImportTypeMapping (type, root, defaultNamespace)); ri.IncludeTypes (t); } } this.xsertype = type; this.overrides = overrides; this.extraTypes = (extraTypes == null ? new Type[0] : extraTypes); if (root != null) this.rootAttribute = root; else { object[] attributes = type.GetCustomAttributes (typeof (XmlRootAttribute), false); if (attributes.Length > 0) this.rootAttribute = (XmlRootAttribute) attributes[0]; } this.defaultNamespace = defaultNamespace; if (typeTable == null) typeTable = new Hashtable (); FillTypeTable (type); } #endregion // Constructors #region Events public event XmlAttributeEventHandler UnknownAttribute; public event XmlElementEventHandler UnknownElement; public event XmlNodeEventHandler UnknownNode; public event UnreferencedObjectEventHandler UnreferencedObject; #endregion // Events #region Properties internal bool UseOrder { get { return useOrder; } set { useOrder = value; } } #endregion // Properties #region Methods [MonoTODO] public virtual bool CanDeserialize (XmlReader xmlReader) { throw new NotImplementedException (); } protected virtual XmlSerializationReader CreateReader () { // This is what MS does!!! throw new NotImplementedException (); } protected virtual XmlSerializationWriter CreateWriter () { // This is what MS does!!! throw new NotImplementedException (); } public object Deserialize (Stream stream) { XmlTextReader xmlReader = new XmlTextReader(stream); return Deserialize(xmlReader); } public object Deserialize (TextReader textReader) { XmlTextReader xmlReader = new XmlTextReader(textReader); return Deserialize(xmlReader); } public bool DeserializeComposite(XmlReader xmlReader, ref Object theObject) { Type objType = theObject.GetType(); bool retVal = true; //Console.WriteLine("DeserializeComposite({0})", objType); // Are we at an empty element? if (xmlReader.IsEmptyElement == true) return retVal; // Read each field, counting how many we find. for (int numFields=0; xmlReader.Read(); ) { XmlNodeType xmlNodeType = xmlReader.NodeType; bool isEmpty = xmlReader.IsEmptyElement; if (xmlNodeType == XmlNodeType.Element) { // Read the field. DeserializeField(xmlReader, ref theObject, xmlReader.Name); numFields++; } else if (xmlNodeType == XmlNodeType.EndElement) { if (numFields == 0) { //Console.WriteLine("Empty object deserialized, ignoring."); retVal = false; } return retVal; } } return retVal; } public void DeserializeField (XmlReader xmlReader, ref Object theObject, String fieldName) { //Console.WriteLine("DeserializeField({0})", fieldName); BindingFlags bindingFlags = 0; Type fieldType = null; // Get the type, first try a field. FieldInfo fieldInfo = theObject.GetType().GetField(fieldName); if (fieldInfo != null) { fieldType = fieldInfo.FieldType; bindingFlags = BindingFlags.SetField; } else { // Is it a property? PropertyInfo propInfo = theObject.GetType().GetProperty(fieldName); if (propInfo != null) { fieldType = propInfo.PropertyType; bindingFlags = BindingFlags.SetProperty; } } if (fieldType == null) throw new Exception (String.Format ("On type {0} can not identify {1}", theObject.GetType (), fieldName)); Object value = null; bool isEmptyField = xmlReader.IsEmptyElement; //Console.WriteLine("DeserializeField({0} of type {1})", fieldName, fieldType); if (fieldType.IsArray && fieldType != typeof(System.Byte[])) { // Create an empty array list. ArrayList list = new ArrayList(); // Call out to deserialize it. DeserializeArray(xmlReader, list, fieldType.GetElementType()); value = list.ToArray(fieldType.GetElementType()); } else if (isEmptyField == false && (IsInbuiltType(fieldType) || fieldType.IsEnum || fieldType.IsArray)) { // Built in, set it. while (xmlReader.Read()) { if (xmlReader.NodeType == XmlNodeType.Text) { //Console.WriteLine(" -> value is '{0}'", xmlReader.Value); if (fieldType == typeof(Guid)) value = XmlConvert.ToGuid(xmlReader.Value); else if (fieldType == typeof(Boolean)) value = XmlConvert.ToBoolean(xmlReader.Value); else if (fieldType == typeof(String)) value = xmlReader.Value; else if (fieldType == typeof(Int64)) value = XmlConvert.ToInt64(xmlReader.Value); else if (fieldType == typeof(DateTime)) value = XmlConvert.ToDateTime(xmlReader.Value); else if (fieldType.IsEnum) value = Enum.Parse(fieldType, xmlReader.Value); else if (fieldType == typeof(System.Byte[])) value = XmlCustomFormatter.ToByteArrayBase64(xmlReader.Value); else if (fieldType == typeof (Int32)) value = XmlConvert.ToInt32 (xmlReader.Value); else Console.WriteLine("XmlSerializer.DeserializeField, improve routine: Error (type is '{0}')", fieldType); break; } } } else if (isEmptyField == true) { if (fieldType.IsArray) { // Must be a byte array, just create an empty one. value = new byte[0]; } else if (fieldType == typeof(string)) { // Create a new empty string. value = ""; } } else { //Console.WriteLine("Creating new {0}", fieldType); // Create the new complex object. value = System.Activator.CreateInstance(fieldType); // Recurse, allowing the method to whack the object if it's empty. DeserializeComposite(xmlReader, ref value); } //Console.WriteLine(" Setting {0} to '{1}'", fieldName, value); // Set the field value. theObject.GetType().InvokeMember(fieldName, bindingFlags, null, theObject, new Object[] { value }, null, null, null); // We need to munch the end. if (IsInbuiltType(fieldType) || fieldType.IsEnum || fieldType == typeof(System.Byte[])) { if (isEmptyField == false) while (xmlReader.Read() && xmlReader.NodeType != XmlNodeType.EndElement) ; } } public void DeserializeArray(XmlReader xmlReader, ArrayList theList, Type theType) { //Console.WriteLine(" DeserializeArray({0})", theType); if (xmlReader.IsEmptyElement) { //Console.WriteLine(" DeserializeArray -> empty, nothing to do here"); return; } while (xmlReader.Read()) { XmlNodeType xmlNodeType = xmlReader.NodeType; bool isEmpty = xmlReader.IsEmptyElement; if (xmlNodeType == XmlNodeType.Element) { // Must be an element of the array, create it. Object obj = System.Activator.CreateInstance(theType); //Console.WriteLine(" created obj of type '{0}'", obj.GetType()); // Deserialize and add. if (DeserializeComposite(xmlReader, ref obj)) { theList.Add(obj); } } if ((xmlNodeType == XmlNodeType.Element && isEmpty) || (xmlNodeType == XmlNodeType.EndElement)) { return; } } } public object Deserialize(XmlReader xmlReader) { Object obj = null; // Read each node in the tree. while (xmlReader.Read()) { if (xmlReader.NodeType == XmlNodeType.Element) { // Create the top level object. //Console.WriteLine("Creating '{0}'", xsertype.FullName); obj = System.Activator.CreateInstance(xsertype); // Deserialize it. DeserializeComposite(xmlReader, ref obj); } else if (xmlReader.NodeType == XmlNodeType.EndElement) { return obj; } } return obj; } protected virtual object Deserialize (XmlSerializationReader reader) { // This is what MS does!!! throw new NotImplementedException (); } [MonoTODO] public static XmlSerializer [] FromMappings (XmlMapping [] mappings) { throw new NotImplementedException (); } [MonoTODO] public static XmlSerializer [] FromTypes (Type [] mappings) { throw new NotImplementedException (); } [MonoTODO] protected virtual void Serialize (object o, XmlSerializationWriter writer) { throw new NotImplementedException (); } public void Serialize (Stream stream, object o) { XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default); xmlWriter.Formatting = Formatting.Indented; Serialize (xmlWriter, o, null); xmlWriter.WriteEndDocument(); xmlWriter.Flush(); } public void Serialize (TextWriter textWriter, object o) { XmlTextWriter xmlWriter = new XmlTextWriter (textWriter); xmlWriter.Formatting = Formatting.Indented; Serialize (xmlWriter, o, null); xmlWriter.WriteEndDocument(); xmlWriter.Flush(); } public void Serialize (XmlWriter xmlWriter, object o) { Serialize (xmlWriter, o, null); } public void Serialize (Stream stream, object o, XmlSerializerNamespaces namespaces) { XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default); xmlWriter.Formatting = Formatting.Indented; Serialize (xmlWriter, o, namespaces); xmlWriter.WriteEndDocument(); xmlWriter.Flush(); } public void Serialize (TextWriter textWriter, object o, XmlSerializerNamespaces namespaces) { XmlTextWriter xmlWriter = new XmlTextWriter (textWriter); xmlWriter.Formatting = Formatting.Indented; Serialize (xmlWriter, o, namespaces); xmlWriter.WriteEndDocument(); xmlWriter.Flush(); } public void Serialize (XmlWriter writer, object o, XmlSerializerNamespaces namespaces) { Type objType = xsertype;//o.GetType (); if (IsInbuiltType(objType)) { if (writer.WriteState == WriteState.Start) writer.WriteStartDocument (); SerializeBuiltIn (writer, o); // Keep WriteState.Content state. // writer.WriteEndDocument(); writer.Flush (); return; } string rootName = objType.Name; string rootNs = String.Empty; string rootPrefix = String.Empty; if (namespaces == null) namespaces = new XmlSerializerNamespaces (); if (namespaces.Count == 0) { namespaces.Add ("xsd", XmlSchema.Namespace); namespaces.Add ("xsi", XmlSchema.InstanceNamespace); } XmlSerializerNamespaces nss = new XmlSerializerNamespaces (); XmlQualifiedName[] qnames; if (writer.WriteState == WriteState.Start) writer.WriteStartDocument (); object [] memberObj = (object []) typeTable [objType]; if (memberObj == null) throw new Exception ("Unknown Type " + objType + " encountered during Serialization"); Hashtable memberTable = (Hashtable) memberObj [0]; XmlAttributes xmlAttributes = (XmlAttributes) memberTable [""]; //If we have been passed an XmlRoot, set it on the base class if (rootAttribute != null) xmlAttributes.XmlRoot = rootAttribute; if (xmlAttributes.XmlRoot != null) { isNullable = xmlAttributes.XmlRoot.IsNullable; if (xmlAttributes.XmlRoot.ElementName != null) rootName = xmlAttributes.XmlRoot.ElementName; rootNs = xmlAttributes.XmlRoot.Namespace; } if (namespaces != null && namespaces.GetPrefix (rootNs) != null) rootPrefix = namespaces.GetPrefix (rootNs); //XMLNS attributes in the Root XmlAttributes XnsAttrs = (XmlAttributes) ((object[]) typeTable[objType])[1]; if (XnsAttrs != null) { MemberInfo member = XnsAttrs.MemberInfo; FieldInfo fieldInfo = member as FieldInfo; PropertyInfo propertyInfo = member as PropertyInfo; XmlSerializerNamespaces xns; if (fieldInfo != null) xns = (XmlSerializerNamespaces) fieldInfo.GetValue (o); else xns = (XmlSerializerNamespaces) propertyInfo.GetValue (o, null); qnames = xns.ToArray (); foreach (XmlQualifiedName qname in qnames) nss.Add (qname.Name, qname.Namespace); } //XmlNs from the namespaces passed qnames = namespaces.ToArray (); foreach (XmlQualifiedName qname in qnames) { if (writer.LookupPrefix (qname.Namespace) != qname.Name) nss.Add (qname.Name, qname.Namespace); } writer.WriteStartElement (rootPrefix, rootName, rootNs); qnames = nss.ToArray(); foreach (XmlQualifiedName qname in qnames) { if (writer.LookupPrefix (qname.Namespace) != qname.Name) writer.WriteAttributeString ("xmlns", qname.Name, null, qname.Namespace); } if (rootPrefix == String.Empty && rootNs != String.Empty && rootNs != null) writer.WriteAttributeString (String.Empty, "xmlns", null, rootNs); SerializeMembers (writer, o, true);//, namespaces); // Keep WriteState.Content state. // writer.WriteEndDocument (); writer.Flush (); } private void SerializeBuiltIn (XmlWriter writer, object o) { if (o is XmlNode) { XmlNode n = (XmlNode) o; XmlNodeReader nrdr = new XmlNodeReader (n); nrdr.Read (); if (nrdr.NodeType == XmlNodeType.XmlDeclaration) nrdr.Read (); do { writer.WriteNode (nrdr, false); } while (nrdr.Read ()); } else { TypeData td = TypeTranslator.GetTypeData (o.GetType ()); writer.WriteStartElement (td.ElementName); WriteBuiltinValue(writer,o); writer.WriteEndElement(); } } private void WriteNilAttribute(XmlWriter writer) { writer.WriteAttributeString("nil",XmlSchema.InstanceNamespace, "true"); } private void WriteBuiltinValue(XmlWriter writer, object o) { if(o == null) WriteNilAttribute(writer); else writer.WriteString (GetXmlValue(o)); } private void SerializeMembers (XmlWriter writer, object o, bool isRoot) { if(o == null) { WriteNilAttribute(writer); return; } Type objType = o.GetType (); if (IsInbuiltType(objType)) { SerializeBuiltIn (writer, o); return; } XmlAttributes nsAttributes = (XmlAttributes) ((object[]) typeTable [objType])[1]; ArrayList attributes = (ArrayList) ((object[]) typeTable [objType])[2]; ArrayList elements = (ArrayList) ((object[]) typeTable [objType])[3]; if (!isRoot && nsAttributes != null) { MemberInfo member = nsAttributes.MemberInfo; FieldInfo fieldInfo = member as FieldInfo; PropertyInfo propertyInfo = member as PropertyInfo; XmlSerializerNamespaces xns; if (fieldInfo != null) xns = (XmlSerializerNamespaces) fieldInfo.GetValue (o); else xns = (XmlSerializerNamespaces) propertyInfo.GetValue (o, null); XmlQualifiedName[] qnames = xns.ToArray (); foreach (XmlQualifiedName qname in qnames) if (writer.LookupPrefix (qname.Namespace) != qname.Name) writer.WriteAttributeString ("xmlns", qname.Name, null, qname.Namespace); } //Serialize the Attributes. foreach (XmlAttributes xmlAttributes in attributes) { MemberInfo member = xmlAttributes.MemberInfo; FieldInfo fieldInfo = member as FieldInfo; PropertyInfo propertyInfo = member as PropertyInfo; Type attributeType; object attributeValue; string attributeValueString; string attributeName; string attributeNs; if (fieldInfo != null) { attributeType = fieldInfo.FieldType; attributeValue = fieldInfo.GetValue (o); } else { attributeType = propertyInfo.PropertyType; attributeValue = propertyInfo.GetValue (o, null); } attributeName = xmlAttributes.GetAttributeName (attributeType, member.Name); attributeNs = xmlAttributes.GetAttributeNamespace (attributeType); if (attributeValue is XmlQualifiedName) { XmlQualifiedName qname = (XmlQualifiedName) attributeValue; if (qname.IsEmpty) continue; writer.WriteStartAttribute (attributeName, attributeNs); writer.WriteQualifiedName (qname.Name, qname.Namespace); writer.WriteEndAttribute (); continue; } else if (attributeValue is XmlQualifiedName[]) { XmlQualifiedName[] qnames = (XmlQualifiedName[]) attributeValue; writer.WriteStartAttribute (attributeName, attributeNs); int count = 0; foreach (XmlQualifiedName qname in qnames) { if (qname.IsEmpty) continue; if (count++ > 0) writer.WriteWhitespace (" "); writer.WriteQualifiedName (qname.Name, qname.Namespace); } writer.WriteEndAttribute (); continue; } else if (attributeValue is XmlAttribute[]) { XmlAttribute[] xmlattrs = (XmlAttribute[]) attributeValue; foreach (XmlAttribute xmlattr in xmlattrs) { xmlattr.WriteTo(writer); } continue; } attributeValueString = GetXmlValue (attributeValue); if (attributeValueString != GetXmlValue (xmlAttributes.XmlDefaultValue)) writer.WriteAttributeString (attributeName, attributeNs, attributeValueString); } // Serialize Elements foreach (XmlAttributes xmlElements in elements) { MemberInfo member = xmlElements.MemberInfo; FieldInfo fieldInfo = member as FieldInfo; PropertyInfo propertyInfo = member as PropertyInfo; Type elementType; object elementValue; string elementName; string elementNs; if (fieldInfo != null) { elementType = fieldInfo.FieldType; elementValue = fieldInfo.GetValue (o); } else { elementType = propertyInfo.PropertyType; elementValue = propertyInfo.GetValue (o, null); } elementName = xmlElements.GetElementName (elementType, member.Name); elementNs = xmlElements.GetElementNamespace (elementType); WriteElement (writer, xmlElements, elementName, elementNs, elementType, elementValue); } } [MonoTODO ("Remove FIXMEs")] private void WriteElement (XmlWriter writer, XmlAttributes attrs, string name, string ns, Type type, Object value) { //IF the element has XmlText Attribute, the name of the member is not serialized; if (attrs.XmlText != null && value != null) { if (type == typeof (object[])) { foreach(object obj in (object[]) value) writer.WriteRaw(""+obj); } else if (type == typeof (string[])) { foreach(string str in (string[]) value) writer.WriteRaw(str); } else if (type == typeof (XmlNode)) { ((XmlNode) value).WriteTo (writer); } else if (type == typeof (XmlNode[])) { XmlNode[] nodes = (XmlNode[]) value; foreach (XmlNode node in nodes) node.WriteTo (writer); } return; } //If not text, serialize as an element //Start the element tag writer.WriteStartElement (name, ns); if (IsInbuiltType (type)) { WriteBuiltinValue(writer,value); } else if (type.IsArray && value != null) { SerializeArray (writer, value); } else if (value is ICollection) { BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; //Find a non indexer Count Property with return type of int PropertyInfo countInfo = type.GetProperty ("Count", flags, null, typeof (int), new Type[0], null); PropertyInfo itemInfo = type.GetProperty ("Item", flags, null, null, new Type[1] {typeof (int)}, null); int count = (int) countInfo.GetValue (value, null); if (count > 0) for (int i = 0; i < count; i++) { object itemValue = itemInfo.GetValue (value, new object[1] {i}); Type itemType = itemInfo.PropertyType; if (itemValue != null) { string itemName = attrs.GetElementName (itemValue.GetType (), TypeTranslator.GetTypeData(itemType).ElementName); string itemNs = attrs.GetElementNamespace (itemValue.GetType ()); writer.WriteStartElement (itemName, itemNs); SerializeMembers (writer, itemValue, false); writer.WriteEndElement (); } } } else if (value is IEnumerable) { // FIXME } else if (type.IsEnum) { writer.WriteString(GetXmlValue(value)); } else { //Complex Type SerializeMembers (writer, value, false); } // Close the Element writer.WriteEndElement(); } //Does not take care of any array specific Xml Attributes [MonoTODO] private void SerializeArray (XmlWriter writer, object o) { Array arr = (o as Array); if(arr == null || arr.Rank != 1) throw new ApplicationException("Expected a single dimension Array, Got "+ o); Type arrayType = arr.GetType().GetElementType(); string arrayTypeName = TypeTranslator.GetTypeData(arrayType).ElementName; TypeData td = TypeTranslator.GetTypeData (arrayType); // Special Treatment for Byte array if(arrayType.Equals(typeof(byte))) { WriteBuiltinValue(writer,o); } else { for(int i=0; i< arr.Length; i++) { writer.WriteStartElement (td.ElementName); object value = arr.GetValue(i); if (IsInbuiltType (arrayType)) { WriteBuiltinValue(writer, value); } else { SerializeMembers(writer, value, false); } writer.WriteEndElement(); } } } /// /// If the type is a string, valuetype or primitive type we do not populate the TypeTable. /// If the type is an array, we populate the TypeTable with Element type of the array. /// If the type implements ICollection, it is handled differently. We do not care for its members. /// If the type implements IEnumberable, we check that it implements Add(). Don't care for members. /// [MonoTODO ("Remove FIXMEs")] private void FillTypeTable (Type type) { // If it's already in the table, don't add it again. if (typeTable.Contains (type)) return; //For value types and strings we don't need the members. //FIXME: We will need the enum types probably. if (IsInbuiltType (type)) return; //Array, ICollection and IEnumberable are treated differenty if (type.IsArray) { FillArrayType (type); return; } else if (type.IsEnum) { FillEnum (type); return; } else { //There must be a public constructor //if (!HasDefaultConstructor (type)) //throw new Exception ("Can't Serialize Type " + type.Name + " since it does not have default Constructor"); if (type.GetInterface ("ICollection") == typeof (System.Collections.ICollection)) { FillICollectionType (type); return; } // if (type.GetInterface ("IDictionary") == typeof (System.Collections.IDictionary)) { // throw new Exception ("Can't Serialize Type " + type.Name + " since it implements IDictionary"); // } if (type.GetInterface ("IEnumerable") == typeof (System.Collections.IEnumerable)) { //FillIEnumerableType(type); //return; } } //Add the Class to the hashtable. //Each value of the hashtable has two objects, one is the hashtable with key of membername (for deserialization) //Other is an Array of XmlSerializernames, Array of XmlAttributes & Array of XmlElements. Object[] memberObj = new Object[4]; typeTable.Add (type,memberObj); Hashtable memberTable = new Hashtable (); memberObj[0] = memberTable; memberTable.Add ("", XmlAttributes.FromClass (type)); memberObj[1] = null; ArrayList attributes = new ArrayList (); memberObj[2] = attributes; ArrayList elements = new ArrayList (); memberObj[3] = elements; //Get the graph of the members. Graph is nothing but the order //in which MS implementation serializes the members. MemberInfo[] minfo = GetGraph (type); foreach (MemberInfo member in minfo) { FieldInfo fieldInfo = (member as FieldInfo); PropertyInfo propertyInfo = (member as PropertyInfo); if (memberTable [member.Name] != null) continue; if (fieldInfo != null) { //If field is readOnly or const, do not serialize it. if (fieldInfo.IsLiteral || fieldInfo.IsInitOnly) continue; XmlAttributes xmlAttributes = XmlAttributes.FromField (member, fieldInfo); //If XmlAttributes have XmlIgnore, ignore this member if (xmlAttributes.XmlIgnore) continue; //If this member is a XmlNs type, set the XmlNs object. if (xmlAttributes.Xmlns) { memberObj[1] = xmlAttributes; continue; } //If the member is a attribute Type, Add to attribute list if (xmlAttributes.isAttribute) attributes.Add (xmlAttributes); else //Add to elements elements.Add (xmlAttributes); //Add in the Hashtable. memberTable.Add (member.Name, xmlAttributes); if (xmlAttributes.XmlAnyAttribute != null || xmlAttributes.XmlText != null) continue; if (xmlAttributes.XmlElements.Count > 0) { foreach (XmlElementAttribute elem in xmlAttributes.XmlElements) { if (elem.Type != null) FillTypeTable (elem.Type); else FillTypeTable (fieldInfo.FieldType); } continue; } if (!IsInbuiltType (fieldInfo.FieldType)) FillTypeTable (fieldInfo.FieldType); } else if (propertyInfo != null) { //If property is readonly or writeonly, do not serialize it. //Exceptions are properties whose return type is array, ICollection or IEnumerable //Indexers are not serialized unless the class Implements ICollection. if (!(propertyInfo.PropertyType.IsArray || Implements (propertyInfo.PropertyType, typeof (ICollection)) || (propertyInfo.PropertyType != typeof (string) && Implements (propertyInfo.PropertyType, typeof (IEnumerable))))) { if(!(propertyInfo.CanRead && propertyInfo.CanWrite) || propertyInfo.GetIndexParameters ().Length != 0) continue; } XmlAttributes xmlAttributes = XmlAttributes.FromProperty (member, propertyInfo); // If XmlAttributes have XmlIgnore, ignore this member if (xmlAttributes.XmlIgnore) continue; // If this member is a XmlNs type, set the XmlNs object. if (xmlAttributes.Xmlns) { memberObj[1] = xmlAttributes; continue; } // If the member is a attribute Type, Add to attribute list if (xmlAttributes.isAttribute) attributes.Add (xmlAttributes); else //Add to elements elements.Add (xmlAttributes); // OtherWise add in the Hashtable. memberTable.Add (member.Name, xmlAttributes); if (xmlAttributes.XmlAnyAttribute != null || xmlAttributes.XmlText != null) continue; if (xmlAttributes.XmlElements.Count > 0) { foreach (XmlElementAttribute elem in xmlAttributes.XmlElements) { if (elem.Type != null) FillTypeTable (elem.Type); else FillTypeTable (propertyInfo.PropertyType); } continue; } if (!IsInbuiltType (propertyInfo.PropertyType)) FillTypeTable (propertyInfo.PropertyType); } } // Sort the attributes for the members according to their Order // This is an extension to MS's Implementation and will be useful // if our reflection does not return the same order of elements // as MS .NET impl if (useOrder) BubbleSort (elements, XmlAttributes.attrComparer); } private void FillArrayType (Type type) { if (type.GetArrayRank () != 1) throw new Exception ("MultiDimensional Arrays are not Supported"); Type arrayType = type.GetElementType (); if (arrayType.IsArray) FillArrayType (arrayType); else if (!IsInbuiltType (arrayType)) FillTypeTable (arrayType); } private void FillICollectionType (Type type) { //Must have an public Indexer that takes an integer and //a public Count Property which returns an int. BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; //Find a non indexer Count Property with return type of int PropertyInfo countProp = type.GetProperty ("Count", flags, null, typeof (int), new Type[0], null); if (countProp == null || !countProp.CanRead) throw new Exception ("Cannot Serialize " + type + " because it implements ICollectoion, but does not implement public Count property"); //Find a indexer Item Property which takes an int PropertyInfo itemProp = type.GetProperty ("Item", flags, null, null, new Type[1] {typeof (int)}, null); if (itemProp == null || !itemProp.CanRead || !itemProp.CanWrite) throw new Exception ("Cannot Serialize " + type + " because it does not have a read/write indexer property that takes an int as argument"); FillTypeTable (itemProp.PropertyType); } [MonoTODO] private void FillIEnumerableType (Type type) { //Must implement a public Add method that takes a single parameter. //The Add method's parameter must be of the same type as is returned from //the Current property on the value returned from GetEnumerator, or one of that type's bases. // We currently ignore enumerable types anyway, so this method was junked. // The code did not do what the documentation above says (if that is even possible!) return; } private void FillEnum (Type type) { Hashtable memberTable = new Hashtable (); BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; typeTable.Add (type, memberTable); string[] names = Enum.GetNames (type); foreach (string name in names) { MemberInfo[] members = type.GetMember (name); if (members.Length != 1) throw new Exception("Should never happen. Enum member not present or more than one. " + name); XmlAttributes xmlAttributes = new XmlAttributes (members[0]); if (xmlAttributes.XmlIgnore) continue; if (xmlAttributes.XmlEnum != null) memberTable.Add (members[0].Name, xmlAttributes.XmlEnum.Name); else memberTable.Add (members[0].Name, members[0].Name); } } private bool HasDefaultConstructor (Type type) { ConstructorInfo defaultConstructor = type.GetConstructor (new Type[0]); if (defaultConstructor == null || defaultConstructor.IsAbstract || defaultConstructor.IsStatic || !defaultConstructor.IsPublic) return false; return true; } private bool IsInbuiltType (Type type) { if (type.IsEnum) return false; if (/* type.IsValueType || */type == typeof (string) || type.IsPrimitive) return true; if (type == typeof (DateTime) || type == typeof (Guid) || type == typeof (XmlNode) || type.IsSubclassOf (typeof (XmlNode))) return true; return false; } private static MemberInfo[] GetGraph(Type type) { ArrayList typeGraph = new ArrayList (); GetGraph (type, typeGraph); return (MemberInfo[]) typeGraph.ToArray (typeof (MemberInfo)); } private static void GetGraph (Type type, ArrayList typeGraph) { BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; if (type.BaseType == null) return; GetGraph (type.BaseType, typeGraph); typeGraph.AddRange (type.GetFields (flags)); typeGraph.AddRange (type.GetProperties (flags)); } private string GetXmlValue (object value) { if (value == null) return null; #region enum type if (value is Enum) { Type type = value.GetType (); if (typeTable.ContainsKey (type)) { Hashtable memberTable = (Hashtable) (typeTable[type]); if (type.IsDefined (typeof (FlagsAttribute), false)) { //If value is exactly a single enum member if (memberTable.Contains (value.ToString ())) return (string) memberTable[value.ToString ()]; string retval = ""; int enumval = (int) value; string[] names = Enum.GetNames (type); foreach (string key in names) { if (!memberTable.ContainsKey (key)) continue; //Otherwise multiple values. int val = (int) Enum.Parse (type, key); if (val != 0 && (enumval & val) == val) retval += " " + (string) memberTable[Enum.GetName (type, val)]; } retval = retval.Trim (); if (retval.Length == 0) return null; return retval; } else if (memberTable.ContainsKey (value.ToString ())) return (string) memberTable[value.ToString()]; else return null; } else throw new Exception ("Unknown Enumeration"); } #endregion if (value is byte[]) return XmlCustomFormatter.FromByteArrayBase64((byte[])value); if (value is Guid) return XmlConvert.ToString((Guid)value); if(value is DateTime) return XmlConvert.ToString((DateTime)value); if(value is TimeSpan) return XmlConvert.ToString((TimeSpan)value); if(value is bool) return XmlConvert.ToString((bool)value); if(value is byte) return XmlConvert.ToString((byte)value); if(value is char) return XmlCustomFormatter.FromChar((char)value); if(value is decimal) return XmlConvert.ToString((decimal)value); if(value is double) return XmlConvert.ToString((double)value); if(value is short) return XmlConvert.ToString((short)value); if(value is int) return XmlConvert.ToString((int)value); if(value is long) return XmlConvert.ToString((long)value); if(value is sbyte) return XmlConvert.ToString((sbyte)value); if(value is float) return XmlConvert.ToString((float)value); if(value is ushort) return XmlConvert.ToString((ushort)value); if(value is uint) return XmlConvert.ToString((uint)value); if(value is ulong) return XmlConvert.ToString((ulong)value); if (value is XmlQualifiedName) { if (((XmlQualifiedName) value).IsEmpty) return null; } return (value == null) ? null : value.ToString (); } [MonoTODO ("Remove FIXMEs")] private static void ProcessAttributes (XmlAttributes attrs, Hashtable memberTable) { if (attrs.XmlAnyAttribute != null) { // FIXME } foreach (XmlAnyElementAttribute anyelem in attrs.XmlAnyElements) memberTable.Add (new XmlQualifiedName (anyelem.Name, anyelem.Namespace), attrs); if (attrs.XmlArray != null) { // FIXME } foreach (XmlArrayItemAttribute item in attrs.XmlArrayItems) memberTable.Add (new XmlQualifiedName (item.ElementName, item.Namespace), attrs); if (attrs.XmlAttribute != null) memberTable.Add (new XmlQualifiedName (attrs.XmlAttribute.AttributeName,attrs.XmlAttribute.Namespace), attrs); if (attrs.XmlChoiceIdentifier != null) { // FIXME } foreach (XmlElementAttribute elem in attrs.XmlElements) memberTable.Add (new XmlQualifiedName (elem.ElementName, elem.Namespace), attrs); if (attrs.XmlEnum != null) { // FIXME } if (attrs.XmlType != null) memberTable.Add (new XmlQualifiedName (attrs.XmlType.TypeName, attrs.XmlType.Namespace), attrs); } private bool Implements (Type type, Type interfaceType) { return (type.GetInterface (interfaceType.Name) == interfaceType); } private static void BubbleSort (ArrayList array, IComparer comparer) { int len = array.Count; object obj1, obj2; for (int i=0; i < len; i++) { for (int j=0; j < len -i -1; j++) { obj1 = array[j]; obj2 = array[j+1]; if (comparer.Compare (obj2 , obj1 ) < 0) { array[j] = obj2; array[j+1] = obj1; } } } } #endregion // Methods } }