2 // Mono Class Libraries
3 // System.Xml.Serialization.XmlSerializer
6 // John Donagher (john@webmeta.com)
7 // Ajay kumar Dwivedi (adwiv@yahoo.com)
8 // Tim Coleman (tim@timcoleman.com)
9 // Elan Feingold (ef10@cornell.edu)
10 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
12 // (C) 2002 John Donagher, Ajay kumar Dwivedi
13 // Copyright (C) Tim Coleman, 2002
14 // (C) 2003 Elan Feingold, Atsushi Enomoto
18 using System.Collections;
20 using System.Reflection;
22 using System.Xml.Schema;
25 namespace System.Xml.Serialization
28 public class XmlSerializer
32 internal class XmlAttributeMapper
\r
34 XmlAttributeOverrides attributeOverrides;
\r
35 Hashtable cachedTypeAttributes = new Hashtable ();
\r
36 Hashtable cachedMemberAttributes = new Hashtable ();
\r
38 public XmlAttributeMapper (XmlAttributeOverrides attributeOverrides)
\r
40 this.attributeOverrides = attributeOverrides;
\r
44 // Get the XmlAttributes object associated with a member
\r
46 public XmlAttributes GetMemberAttributes (MemberInfo member)
\r
48 XmlAttributes cache = cachedMemberAttributes [member] as XmlAttributes;
\r
52 XmlAttributes attribs = null;
\r
55 if (attributeOverrides != null)
\r
56 attribs = attributeOverrides [member.ReflectedType, member.Name];
\r
57 if (attribs == null) {
\r
58 attribs = new XmlAttributes ();
\r
59 object [] attributes = member.GetCustomAttributes (false);
\r
60 attribs.AddMemberAttributes (attributes);
\r
63 attribs.MemberInfo = member;
\r
64 attribs.PropertyInfo = (member as PropertyInfo);
\r
65 attribs.FieldInfo = (member as FieldInfo);
\r
68 cachedTypeAttributes [member] = attribs;
\r
73 // Get a types XmlAttributes
\r
75 public XmlAttributes GetTypeAttributes (Type type)
\r
77 XmlAttributes cache = cachedTypeAttributes [type] as XmlAttributes;
\r
81 XmlAttributes attribs = null;
\r
84 if (attributeOverrides != null) {
\r
85 attribs = attributeOverrides [type];
\r
86 if (attribs != null)
\r
89 attribs = XmlAttributes.FromClass (type);
\r
90 if (attribs != null)
\r
92 return new XmlAttributes ();
\r
94 cachedTypeAttributes [type] = attribs;
\r
103 XmlAttributeOverrides overrides;
105 XmlRootAttribute rootAttribute;
106 string defaultNamespace;
107 TypeTablePool typeTable;
110 Hashtable typeMappings = new Hashtable ();
111 TypeTranslator typeTranslator = new TypeTranslator ();
112 XmlAttributeMapper attributeMapper;
118 protected XmlSerializer ()
122 public XmlSerializer (Type type)
123 : this (type, null, null, null, null)
127 public XmlSerializer (XmlTypeMapping xmlTypeMapping)
129 typeMappings.Add (xmlTypeMapping.TypeFullName, xmlTypeMapping);
132 public XmlSerializer (Type type, string defaultNamespace)
133 : this (type, null, null, null, defaultNamespace)
137 public XmlSerializer (Type type, Type[] extraTypes)
138 : this (type, null, extraTypes, null, null)
142 public XmlSerializer (Type type, XmlAttributeOverrides overrides)
143 : this (type, overrides, null, null, null)
147 public XmlSerializer (Type type, XmlRootAttribute root)
148 : this (type, null, null, root, null)
152 // internal XmlSerializer (Hashtable typeTable)
154 // this.typeTable = typeTable;
157 public XmlSerializer (Type type,
158 XmlAttributeOverrides overrides,
160 XmlRootAttribute root,
161 string defaultNamespace)
164 throw new ArgumentNullException ("type");
166 // TODO: the *REAL* construction steps are:
167 // (1)create XmlReflectionMember list
168 // for each serializable members.
169 // (2)use XmlReflectionImporter.ImportMembersMapping()
170 // to generate XmlMembersMapping.
171 attributeMapper = new XmlAttributeMapper (overrides);
173 XmlReflectionImporter ri = new XmlReflectionImporter (overrides, defaultNamespace);
174 TypeData td = typeTranslator.GetTypeData (type);
175 typeMappings.Add (td.FullTypeName, ri.ImportTypeMapping (type, root, defaultNamespace));
176 ri.IncludeTypes (type);
178 if (extraTypes != null) {
179 foreach (Type t in extraTypes) {
180 td = typeTranslator.GetTypeData (t);
181 string n = td.FullTypeName;
182 typeMappings.Add (n, ri.ImportTypeMapping (type, root, defaultNamespace));
187 this.xsertype = type;
188 this.overrides = overrides;
189 this.extraTypes = (extraTypes == null ? new Type[0] : extraTypes);
192 this.rootAttribute = root;
194 XmlAttributes attributes = new XmlAttributes (type);
195 if (attributes.XmlRoot != null)
196 this.rootAttribute = attributes.XmlRoot;
199 this.defaultNamespace = defaultNamespace;
201 if (typeTable == null)
202 typeTable = new TypeTablePool ();
204 FillTypeTable (type);
207 #endregion // Constructors
211 public event XmlAttributeEventHandler UnknownAttribute;
212 public event XmlElementEventHandler UnknownElement;
213 public event XmlNodeEventHandler UnknownNode;
214 public event UnreferencedObjectEventHandler UnreferencedObject;
220 internal bool UseOrder {
221 get { return useOrder; }
222 set { useOrder = value; }
225 #endregion // Properties
229 [MonoTODO ("Implement.")]
230 public virtual bool CanDeserialize (XmlReader xmlReader)
232 string localName = (rootAttribute != null ? rootAttribute.ElementName : xsertype.Name);
233 // throw new NotImplementedException ();
234 xmlReader.MoveToContent ();
235 return localName == xmlReader.LocalName;
238 protected virtual XmlSerializationReader CreateReader ()
240 // This is what MS does.
241 // Its body is defined only in extended classes.
242 throw new NotImplementedException ();
245 protected virtual XmlSerializationWriter CreateWriter ()
247 // This is what MS does.
248 // Its body is defined only in extended classes.
249 throw new NotImplementedException ();
252 public object Deserialize (Stream stream)
254 XmlTextReader xmlReader = new XmlTextReader(stream);
255 return Deserialize(xmlReader);
258 public object Deserialize (TextReader textReader)
260 XmlTextReader xmlReader = new XmlTextReader(textReader);
261 return Deserialize(xmlReader);
264 public bool DeserializeComposite(XmlReader xmlReader, ref Object theObject)
266 Type objType = theObject.GetType();
269 // Are we at an empty element?
270 if (xmlReader.IsEmptyElement == true)
273 int startDepth = xmlReader.Depth;
275 if (xmlReader.NodeType == XmlNodeType.EndElement)
278 // Read each field, counting how many we find.
279 for (int numFields=0; xmlReader.Depth > startDepth; xmlReader.Read()) {
280 switch (xmlReader.NodeType) {
281 case XmlNodeType.Element:
283 DeserializeField(xmlReader, ref theObject, xmlReader.Name);
286 case XmlNodeType.EndElement:
295 private void DeserializeField (XmlReader xmlReader, ref Object theObject, String fieldName)
297 //Console.WriteLine("DeserializeField({0})", fieldName);
299 BindingFlags bindingFlags = 0;
300 Type fieldType = null;
302 // Get the type, first try a field.
303 FieldInfo fieldInfo = theObject.GetType().GetField(fieldName);
305 if (fieldInfo != null) {
306 fieldType = fieldInfo.FieldType;
307 bindingFlags = BindingFlags.SetField;
310 PropertyInfo propInfo = theObject.GetType().GetProperty(fieldName);
311 if (propInfo != null)
313 fieldType = propInfo.PropertyType;
314 bindingFlags = BindingFlags.SetProperty;
318 if (fieldType == null)
319 throw new Exception (String.Format ("On type {0} can not identify {1}", theObject.GetType (), fieldName));
322 bool isEmptyField = xmlReader.IsEmptyElement;
324 //Console.WriteLine("DeserializeField({0} of type {1})", fieldName, fieldType);
326 if (fieldType.IsArray && fieldType != typeof(System.Byte[]))
328 // Create an empty array list.
329 ArrayList list = new ArrayList();
331 // Call out to deserialize it.
332 DeserializeArray(xmlReader, list, fieldType.GetElementType());
333 value = list.ToArray(fieldType.GetElementType());
335 else if (isEmptyField == false && (IsInbuiltType(fieldType) || fieldType.IsEnum || fieldType.IsArray))
338 while (xmlReader.Read())
340 if (xmlReader.NodeType == XmlNodeType.Text)
342 //Console.WriteLine(" -> value is '{0}'", xmlReader.Value);
344 if (fieldType == typeof(Guid))
345 value = XmlConvert.ToGuid(xmlReader.Value);
346 else if (fieldType == typeof(Boolean))
347 value = XmlConvert.ToBoolean(xmlReader.Value);
348 else if (fieldType == typeof(String))
349 value = xmlReader.Value;
350 else if (fieldType == typeof(Int64))
351 value = XmlConvert.ToInt64(xmlReader.Value);
352 else if (fieldType == typeof(DateTime))
353 value = XmlConvert.ToDateTime(xmlReader.Value);
354 else if (fieldType.IsEnum)
355 value = Enum.Parse(fieldType, xmlReader.Value);
356 else if (fieldType == typeof(System.Byte[]))
357 value = XmlCustomFormatter.ToByteArrayBase64(xmlReader.Value);
358 else if (fieldType == typeof (Int32))
359 value = XmlConvert.ToInt32 (xmlReader.Value);
361 throw new NotImplementedException (String.Format ("XmlSerializer.DeserializeField, improve routine: Error (type is '{0}')", fieldType));
367 else if (isEmptyField == true)
369 if (fieldType.IsArray)
371 // Must be a byte array, just create an empty one.
374 else if (fieldType == typeof(string))
376 // Create a new empty string.
382 //Console.WriteLine("Creating new {0}", fieldType);
384 // Create the new complex object.
385 value = System.Activator.CreateInstance(fieldType);
387 // Recurse, allowing the method to whack the object if it's empty.
388 DeserializeComposite(xmlReader, ref value);
391 //Console.WriteLine(" Setting {0} to '{1}'", fieldName, value);
393 // Set the field value.
394 theObject.GetType().InvokeMember(fieldName,
398 new Object[] { value },
401 // We need to munch the end.
402 if (IsInbuiltType(fieldType) ||
404 fieldType == typeof(System.Byte[]))
406 if (isEmptyField == false)
407 while (xmlReader.Read() && xmlReader.NodeType != XmlNodeType.EndElement)
413 public void DeserializeArray(XmlReader xmlReader, ArrayList theList, Type theType)
415 //Console.WriteLine(" DeserializeArray({0})", theType);
417 if (xmlReader.IsEmptyElement)
419 //Console.WriteLine(" DeserializeArray -> empty, nothing to do here");
423 while (xmlReader.Read())
425 XmlNodeType xmlNodeType = xmlReader.NodeType;
426 bool isEmpty = xmlReader.IsEmptyElement;
428 if (xmlNodeType == XmlNodeType.Element)
430 // Must be an element of the array, create it.
431 Object obj = System.Activator.CreateInstance(theType);
433 //Console.WriteLine(" created obj of type '{0}'", obj.GetType());
435 // Deserialize and add.
436 if (DeserializeComposite(xmlReader, ref obj))
442 if ((xmlNodeType == XmlNodeType.Element && isEmpty) ||
443 (xmlNodeType == XmlNodeType.EndElement))
450 [MonoTODO ("Use XmlSerializationReader and extended XmlSerializer.")]
451 public object Deserialize (XmlReader xmlReader)
453 if (!CanDeserialize (xmlReader))
454 throw new InvalidOperationException ("Cannot deserialize specified Xml.");
458 TypeData td = typeTranslator.GetTypeData (xsertype);
459 if (td.Type.IsArray) {
460 ArrayList list = new ArrayList ();
461 // Call out to deserialize it.
462 DeserializeArray (xmlReader, list, xsertype.GetElementType ());
463 obj = list.ToArray (xsertype.GetElementType ());
465 // Create the top level object.
466 obj = System.Activator.CreateInstance(xsertype);
467 if (!xmlReader.IsEmptyElement)
468 DeserializeComposite (xmlReader, ref obj);
473 protected virtual object Deserialize (XmlSerializationReader reader)
475 // This is what MS does.
476 // Because it is defined only in extended classes.
477 throw new NotImplementedException ();
481 public static XmlSerializer [] FromMappings (XmlMapping [] mappings)
483 throw new NotImplementedException ();
487 public static XmlSerializer [] FromTypes (Type [] mappings)
489 throw new NotImplementedException ();
492 protected virtual void Serialize (object o, XmlSerializationWriter writer)
494 // Because it is defined only in extended classes.
495 throw new NotImplementedException ();
498 public void Serialize (Stream stream, object o)
500 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
501 xmlWriter.Formatting = Formatting.Indented;
502 xmlWriter.WriteStartDocument ();
503 Serialize (xmlWriter, o, null);
504 xmlWriter.WriteEndDocument();
508 public void Serialize (TextWriter textWriter, object o)
510 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
511 xmlWriter.Formatting = Formatting.Indented;
512 xmlWriter.WriteStartDocument ();
513 Serialize (xmlWriter, o, null);
514 xmlWriter.WriteEndDocument();
518 public void Serialize (XmlWriter xmlWriter, object o)
520 Serialize (xmlWriter, o, null);
523 public void Serialize (Stream stream, object o, XmlSerializerNamespaces namespaces)
525 XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
526 xmlWriter.Formatting = Formatting.Indented;
527 xmlWriter.WriteStartDocument ();
528 Serialize (xmlWriter, o, namespaces);
529 xmlWriter.WriteEndDocument();
533 public void Serialize (TextWriter textWriter, object o, XmlSerializerNamespaces namespaces)
535 XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
536 xmlWriter.Formatting = Formatting.Indented;
537 xmlWriter.WriteStartDocument ();
538 Serialize (xmlWriter, o, namespaces);
539 xmlWriter.WriteEndDocument();
543 [MonoTODO ("Use XmlSerializationWriter")]
544 public void Serialize (XmlWriter writer, object o, XmlSerializerNamespaces namespaces)
546 if (IsInbuiltType(xsertype)) {
547 SerializeBuiltIn (writer, o, xsertype);
551 // FIXME: If XmlRoot specifies IsNullable=true,
552 // then it must emit xsi:nil instance.
554 throw new ArgumentNullException ("object o");
556 // First, get root type mapping.
557 TypeTableEntry entry = typeTable [xsertype];
558 Hashtable memberTable = entry.MemberTable;
559 XmlAttributes xmlAttributes = (XmlAttributes) memberTable [""];
560 bool isNullable = false;
561 XmlRootAttribute root = xmlAttributes.XmlRoot;
563 isNullable = root.IsNullable;
567 throw new ArgumentNullException ("target object is null.");
569 this.WriteElement (writer, xmlAttributes, root.ElementName, root.Namespace, xsertype, null, namespaces);
574 // We don't use actual (derived)Type, which may have
575 // its own, different XmlAttributes.
576 if (!xsertype.IsInstanceOfType (o))
577 throw new InvalidOperationException ("Specified object is not of the target Type.");
579 if (namespaces == null)
580 namespaces = new XmlSerializerNamespaces ();
581 namespaces.Add ("xsd", XmlSchema.Namespace);
582 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
584 if (writer.WriteState == WriteState.Start)
585 writer.WriteStartDocument ();
586 SerializeType (writer, o, namespaces, xsertype);
587 // Keep WriteState.Content state.
588 // Don't WriteEndDocument().
592 private void SerializeType (XmlWriter writer, object o, XmlSerializerNamespaces namespaces, Type objType)
594 if (IsInbuiltType(objType))
596 SerializeBuiltIn (writer, o, objType);
600 XmlSerializerNamespaces nss =
601 new XmlSerializerNamespaces ();
603 XmlQualifiedName[] qnames;
605 TypeTableEntry entry = (TypeTableEntry) typeTable [objType];
607 throw new Exception ("Unknown Type " + objType +
608 " encountered during Serialization");
610 Hashtable memberTable = entry.MemberTable;
611 XmlAttributes xmlAttributes = (XmlAttributes) memberTable [""];
613 string rootName = objType.Name;
614 string rootNs = String.Empty;
615 string rootPrefix = String.Empty;
617 //If we have been passed an XmlRoot, set it on the base class
618 if (rootAttribute != null)
619 xmlAttributes.XmlRoot = rootAttribute;
621 if (xmlAttributes.XmlRoot != null) {
622 isNullable = xmlAttributes.XmlRoot.IsNullable;
623 if (xmlAttributes.XmlRoot.ElementName != null)
624 rootName = xmlAttributes.XmlRoot.ElementName;
625 rootNs = xmlAttributes.XmlRoot.Namespace;
628 if (namespaces != null && namespaces.GetPrefix (rootNs) != null)
629 rootPrefix = namespaces.GetPrefix (rootNs);
631 //XMLNS attributes in the Root
632 XmlAttributes XnsAttrs = ((TypeTableEntry) typeTable [objType]).XmlAttributes;
634 if (XnsAttrs != null) {
635 MemberInfo member = XnsAttrs.MemberInfo;
636 FieldInfo fieldInfo = member as FieldInfo;
637 PropertyInfo propertyInfo = member as PropertyInfo;
638 XmlSerializerNamespaces xns;
640 if (fieldInfo != null)
641 xns = (XmlSerializerNamespaces) fieldInfo.GetValue (o);
643 xns = (XmlSerializerNamespaces) propertyInfo.GetValue (o, null);
645 qnames = xns.ToArray ();
647 foreach (XmlQualifiedName qname in qnames)
648 nss.Add (qname.Name, qname.Namespace);
651 //XmlNs from the namespaces passed
652 qnames = namespaces.ToArray ();
653 foreach (XmlQualifiedName qname in qnames) {
654 if (writer.LookupPrefix (qname.Namespace) != qname.Name)
655 nss.Add (qname.Name, qname.Namespace);
658 writer.WriteStartElement (rootPrefix, rootName, rootNs);
660 qnames = nss.ToArray();
661 foreach (XmlQualifiedName qname in qnames) {
662 if (writer.LookupPrefix (qname.Namespace) != qname.Name)
663 writer.WriteAttributeString ("xmlns", qname.Name, null, qname.Namespace);
666 if (rootPrefix == String.Empty && rootNs != String.Empty && rootNs != null)
667 writer.WriteAttributeString (String.Empty, "xmlns", null, rootNs);
669 SerializeMembers (writer, o, true, namespaces);
672 // TODO: Resolve custom XmlAttributes
673 private void SerializeBuiltIn (XmlWriter writer, object o, Type type)
675 if (o is XmlQualifiedName) {
676 XmlQualifiedName qn = o as XmlQualifiedName;
677 if (writer.WriteState != WriteState.Attribute) {
678 writer.WriteStartElement ("QName");
679 writer.WriteStartAttribute ("xmlns", "q1", "http://www.w3.org/2000/xmlns");
680 writer.WriteString (qn.Namespace);
681 writer.WriteEndAttribute ();
682 writer.WriteQualifiedName (qn.Name, qn.Namespace);
683 writer.WriteEndElement ();
685 writer.WriteQualifiedName (qn.Name, qn.Namespace);
686 } else if (o is XmlNode) {
687 XmlNode n = (XmlNode) o;
688 XmlNodeReader nrdr = new XmlNodeReader (n);
689 nrdr.MoveToContent ();
690 writer.WriteNode (nrdr, false);
692 TypeData td = typeTranslator.GetTypeData (type);
693 writer.WriteStartElement (td.ElementName);
695 WriteNilAttribute (writer);
697 WriteBuiltinValue (writer,o);
698 writer.WriteEndElement();
702 private void WriteNilAttribute(XmlWriter writer)
704 writer.WriteAttributeString("nil",XmlSchema.InstanceNamespace, "true");
707 private void WriteBuiltinValue (XmlWriter writer, object o)
710 WriteNilAttribute (writer);
712 writer.WriteString (GetXmlValue(o));
715 private void SerializeMembers (XmlWriter writer, object o, bool isRoot, XmlSerializerNamespaces namespaces)
719 WriteNilAttribute (writer);
723 Type objType = o.GetType ();
725 if (IsInbuiltType(objType))
727 SerializeBuiltIn (writer, o, objType);
731 TypeTableEntry entry = (TypeTableEntry) typeTable [objType];
732 XmlAttributes nsAttributes = entry.XmlAttributes;
733 ArrayList attributes = entry.AttributeMembers;
734 ArrayList elements = entry.ElementMembers;
736 if (!isRoot && nsAttributes != null) {
737 MemberInfo member = nsAttributes.MemberInfo;
738 FieldInfo fieldInfo = member as FieldInfo;
739 PropertyInfo propertyInfo = member as PropertyInfo;
741 XmlSerializerNamespaces xns;
743 if (fieldInfo != null)
744 xns = (XmlSerializerNamespaces) fieldInfo.GetValue (o);
746 xns = (XmlSerializerNamespaces) propertyInfo.GetValue (o, null);
748 XmlQualifiedName[] qnames = xns.ToArray ();
749 foreach (XmlQualifiedName qname in qnames)
750 if (writer.LookupPrefix (qname.Namespace) != qname.Name)
751 writer.WriteAttributeString ("xmlns", qname.Name, null, qname.Namespace);
754 //Serialize the Attributes.
755 foreach (XmlAttributes xmlAttributes in attributes) {
756 MemberInfo member = xmlAttributes.MemberInfo;
757 FieldInfo fieldInfo = member as FieldInfo;
758 PropertyInfo propertyInfo = member as PropertyInfo;
761 object attributeValue;
762 string attributeValueString;
763 string attributeName;
766 if (fieldInfo != null) {
767 attributeValue = fieldInfo.GetValue (o);
768 attributeType = (attributeValue == null) ? fieldInfo.FieldType : attributeValue.GetType ();
771 attributeValue = propertyInfo.GetValue (o, null);
772 attributeType = (attributeValue == null) ? propertyInfo.PropertyType : attributeValue.GetType ();
775 attributeName = xmlAttributes.GetAttributeName (attributeType, member.Name);
776 attributeNs = xmlAttributes.GetAttributeNamespace (attributeType);
778 if (attributeValue is XmlQualifiedName) {
779 XmlQualifiedName qname = (XmlQualifiedName) attributeValue;
784 writer.WriteStartAttribute (attributeName, attributeNs);
785 writer.WriteQualifiedName (qname.Name, qname.Namespace);
786 writer.WriteEndAttribute ();
789 else if (attributeValue is XmlQualifiedName[]) {
790 XmlQualifiedName[] qnames = (XmlQualifiedName[]) attributeValue;
791 writer.WriteStartAttribute (attributeName, attributeNs);
793 foreach (XmlQualifiedName qname in qnames) {
797 writer.WriteWhitespace (" ");
798 writer.WriteQualifiedName (qname.Name, qname.Namespace);
800 writer.WriteEndAttribute ();
803 else if (attributeValue is XmlAttribute[]) {
804 XmlAttribute[] xmlattrs = (XmlAttribute[]) attributeValue;
805 foreach (XmlAttribute xmlattr in xmlattrs) {
806 xmlattr.WriteTo(writer);
811 attributeValueString = GetXmlValue (attributeValue);
812 if (attributeValueString != GetXmlValue (xmlAttributes.XmlDefaultValue))
813 writer.WriteAttributeString (attributeName, attributeNs, attributeValueString);
816 // Serialize Elements
817 foreach (XmlAttributes xmlElements in elements) {
818 MemberInfo member = xmlElements.MemberInfo;
819 FieldInfo fieldInfo = member as FieldInfo;
820 PropertyInfo propertyInfo = member as PropertyInfo;
827 if (fieldInfo != null) {
828 elementValue = fieldInfo.GetValue (o);
829 elementType = (elementValue == null) ? fieldInfo.FieldType : elementValue.GetType ();
832 elementValue = propertyInfo.GetValue (o, null);
833 elementType = (elementValue == null) ? propertyInfo.PropertyType : elementValue.GetType ();
836 if (elementType.GetInterface (typeof (ICollection).FullName) != null)
837 this.WriteCollectionElementMember (writer, member.Name, xmlElements, elementType, elementValue, namespaces);
839 elementName = xmlElements.GetElementName (elementType, member.Name);
840 elementNs = xmlElements.GetElementNamespace (elementType);
842 if (elementValue == null &&
843 !xmlElements.GetElementIsNullable (elementType))
844 continue; // do not serialize
846 WriteElement (writer, xmlElements, elementName, elementNs, elementType, elementValue, namespaces);
851 private void WriteCollectionElementMember (XmlWriter writer, string elementDefaultName, XmlAttributes attrs, Type type, Object value, XmlSerializerNamespaces namespaces)
853 if (attrs.XmlElements != null && attrs.XmlElements.Count > 0)
854 SerializeCollectionContent (writer, attrs, type, value, namespaces);
858 if (attrs.XmlArray != null) {
859 elementName = attrs.XmlArray.ElementName;
860 elementNs = attrs.XmlArray.Namespace;
862 elementName = attrs.GetElementName (type,elementDefaultName);
863 elementNs = attrs.GetElementNamespace (type);
865 WriteElement (writer, attrs, elementName, elementNs, type, value, namespaces);
869 [MonoTODO ("Remove FIXMEs")]
870 private void WriteElement (XmlWriter writer, XmlAttributes attrs, string name, string ns, Type type, Object value, XmlSerializerNamespaces namespaces)
872 //IF the element has XmlText Attribute, the name of the member is not serialized;
873 if (attrs.XmlText != null && value != null)
875 if (type == typeof (object[]))
877 foreach(object obj in (object[]) value)
878 writer.WriteRaw(""+obj);
880 else if (type == typeof (string[]))
882 foreach(string str in (string[]) value)
883 writer.WriteRaw(str);
885 else if (type == typeof (XmlNode))
887 ((XmlNode) value).WriteTo (writer);
889 else if (type == typeof (XmlNode[]))
891 XmlNode[] nodes = (XmlNode[]) value;
892 foreach (XmlNode node in nodes)
893 node.WriteTo (writer);
898 //If not text, serialize as an element
900 //Start the element tag
901 writer.WriteStartElement (name, ns);
903 if (IsInbuiltType (type))
905 WriteBuiltinValue(writer,value);
907 else if (type.IsArray && value != null)
909 SerializeArrayContent (writer, value, namespaces);
911 else if (value is ICollection)
913 SerializeCollectionContent (writer, attrs, type, value, namespaces);
915 else if (value is IEnumerable)
918 throw new NotImplementedException ();
920 else if (type.IsEnum)
922 writer.WriteString(GetXmlValue(value));
926 SerializeMembers (writer, value, false, namespaces);
930 writer.WriteEndElement();
933 private void SerializeCollectionContent (XmlWriter writer, XmlAttributes attrs, Type type, object value, XmlSerializerNamespaces namespaces)
935 BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
937 //Find a non indexer Count Property with return type of int
938 PropertyInfo countInfo = type.GetProperty ("Count", flags, null, typeof (int), new Type[0], null);
939 PropertyInfo itemInfo = type.GetProperty ("Item", flags, null, null, new Type[1] {typeof (int)}, null);
940 int count = (int) countInfo.GetValue (value, null);
943 for (int i = 0; i < count; i++) {
944 object itemValue = itemInfo.GetValue (value, new object[1] {i});
945 Type itemType = itemInfo.PropertyType;
947 string itemName = attrs.GetElementName (itemValue != null ? itemValue.GetType () : itemType, typeTranslator.GetTypeData(itemType).ElementName);
948 string itemNs = attrs.GetElementNamespace (itemValue != null ? itemValue.GetType () : itemType);
949 if (itemValue != null) {
950 writer.WriteStartElement (itemName, itemNs);
951 SerializeMembers (writer, itemValue, false, namespaces);
952 writer.WriteEndElement ();
954 foreach (XmlElementAttribute elem in attrs.XmlElements) {
955 if (elem.IsNullable) {
956 writer.WriteStartElement (itemName, itemNs);
957 WriteNilAttribute (writer);
958 writer.WriteEndElement ();
966 //Does not take care of any array specific Xml Attributes
968 private void SerializeArrayContent (XmlWriter writer, object o, XmlSerializerNamespaces namespaces)
970 Array arr = (o as Array);
971 if(arr == null || arr.Rank != 1)
972 throw new ApplicationException("Expected a single dimension Array, Got "+ o);
974 Type arrayType = arr.GetType().GetElementType();
975 string arrayTypeName = typeTranslator.GetTypeData(arrayType).ElementName;
977 TypeData td = typeTranslator.GetTypeData (arrayType);
979 // Special Treatment for Byte array
980 if(arrayType.Equals(typeof(byte)))
982 WriteBuiltinValue(writer,o);
986 for(int i=0; i< arr.Length; i++)
988 writer.WriteStartElement (td.ElementName);
989 object value = arr.GetValue(i);
990 if (IsInbuiltType (arrayType))
992 WriteBuiltinValue(writer, value);
996 SerializeMembers(writer, value, false, namespaces);
999 writer.WriteEndElement();
1004 private bool IsFieldTypeSerializable (FieldInfo fieldInfo)
1006 //If field is readOnly or const, do not serialize it.
1007 if (fieldInfo.IsLiteral || fieldInfo.IsInitOnly)
1009 // If no default constructor, do not serialize it.
1010 if (!HasDefaultConstructor (fieldInfo.FieldType))
1015 private bool IsTypeArrayOrSpecial (Type type)
1017 return type.IsArray ||
1018 Implements (type, typeof (ICollection)) ||
1019 (type != typeof (string) && Implements (type, typeof (IEnumerable)));
1022 private bool IsPropertyTypeSerializable (PropertyInfo propertyInfo)
1024 //If property is readonly or writeonly, do not serialize.
1025 //Exceptions are properties whose return type is array,
1026 //ICollection or IEnumerable.
1027 //Indexers are not serialized unless the class
1028 //Implements ICollection.
1029 if (!IsTypeArrayOrSpecial (propertyInfo.PropertyType)) {
1030 if(!(propertyInfo.CanRead && propertyInfo.CanWrite) ||
1031 propertyInfo.GetIndexParameters ().Length != 0)
1039 /// If the type is a string, valuetype or primitive type we do not populate the TypeTable.
1040 /// If the type is an array, we populate the TypeTable with Element type of the array.
1041 /// If the type implements ICollection, it is handled differently. We do not care for its members.
1042 /// If the type implements IEnumberable, we check that it implements Add(). Don't care for members.
1044 [MonoTODO ("Remove FIXMEs")]
1045 private void FillTypeTable (Type type)
1047 // If it's already in the table, don't add it again.
1048 if (typeTable.Contains (type))
1051 //For value types and strings we don't need the members.
1052 //FIXME: We will need the enum types probably.
1053 if (IsInbuiltType (type))
1056 //Array, ICollection and IEnumberable are treated differenty
1058 FillArrayType (type);
1061 else if (type.IsEnum) {
1066 //There must be a public constructor
1067 if (!HasDefaultConstructor (type) && !IsTypeArrayOrSpecial (type))
1068 throw new Exception ("Can't Serialize Type " + type.Name + " since it does not have default Constructor");
1070 if (type.GetInterface ("ICollection") == typeof (System.Collections.ICollection)) {
1071 FillICollectionType (type);
1074 // if (type.GetInterface ("IDictionary") == typeof (System.Collections.IDictionary)) {
1075 // throw new Exception ("Can't Serialize Type " + type.Name + " since it implements IDictionary");
1077 if (type.GetInterface ("IEnumerable") == typeof (System.Collections.IEnumerable)) {
1078 //FillIEnumerableType(type);
1084 //Add the Class to the hashtable.
1085 //Each value of the hashtable has two objects, one is the hashtable with key of membername (for deserialization)
1086 //Other is an Array of XmlSerializernames, Array of XmlAttributes & Array of XmlElements.
1087 Hashtable memberTable = new Hashtable ();
1088 ArrayList attributes = new ArrayList ();
1089 ArrayList elements = new ArrayList ();
1091 TypeTableEntry entry = new TypeTableEntry (memberTable, null, elements, attributes);
1092 typeTable.Add (type, entry);
1094 XmlAttributes resolvedAttributes =
1095 attributeMapper.GetTypeAttributes (type);
1096 memberTable.Add ("", resolvedAttributes);
1098 //Get the graph of the members. Graph is nothing but the order
1099 //in which MS implementation serializes the members.
1100 MemberInfo[] minfo = GetGraph (type);
1102 foreach (MemberInfo member in minfo) {
1103 FieldInfo fieldInfo = (member as FieldInfo);
1104 PropertyInfo propertyInfo = (member as PropertyInfo);
1106 if (memberTable [member.Name] != null)
1109 if (fieldInfo != null) {
1110 if (!IsFieldTypeSerializable (fieldInfo))
1113 XmlAttributes xmlAttributes = XmlAttributes.FromField (member, fieldInfo);
1115 //If XmlAttributes have XmlIgnore, ignore this member
1117 if (xmlAttributes.XmlIgnore)
1120 //If this member is a XmlNs type, set the XmlNs object.
1121 if (xmlAttributes.Xmlns) {
1122 entry.XmlAttributes = xmlAttributes;
1126 //If the member is a attribute Type, Add to attribute list
1127 if (xmlAttributes.isAttribute)
1128 attributes.Add (xmlAttributes);
1129 else //Add to elements
1130 elements.Add (xmlAttributes);
1132 //Add in the Hashtable.
1133 memberTable.Add (member.Name, xmlAttributes);
1135 if (xmlAttributes.XmlAnyAttribute != null || xmlAttributes.XmlText != null)
1138 if (xmlAttributes.XmlElements.Count > 0) {
1139 foreach (XmlElementAttribute elem in xmlAttributes.XmlElements) {
1140 if (elem.Type != null)
1141 FillTypeTable (elem.Type);
1143 FillTypeTable (fieldInfo.FieldType);
1148 if (!IsInbuiltType (fieldInfo.FieldType))
1149 FillTypeTable (fieldInfo.FieldType);
1151 else if (propertyInfo != null) {
1153 XmlAttributes xmlAttributes = XmlAttributes.FromProperty (member, propertyInfo);
1155 // If XmlAttributes have XmlIgnore, ignore this member
1156 if (xmlAttributes.XmlIgnore)
1159 bool anySerializable = false;
1160 if (xmlAttributes.XmlIncludes != null) {
1161 foreach (XmlIncludeAttribute inc in xmlAttributes.XmlIncludes) {
1162 if (HasDefaultConstructor (inc.Type)) {
1163 anySerializable = true;
1164 FillTypeTable (inc.Type);
1168 // If property's Types (containing any
1169 // included types) have no default
1170 // constructor, do not serialize it.
1171 if (!IsPropertyTypeSerializable (propertyInfo) && !HasDefaultConstructor (propertyInfo.PropertyType) && !anySerializable)
1174 // If this member is a XmlNs type, set the XmlNs object.
1175 if (xmlAttributes.Xmlns) {
1176 entry.XmlAttributes = xmlAttributes;
1179 // If the member is a attribute Type, Add to attribute list
1180 if (xmlAttributes.isAttribute)
1181 attributes.Add (xmlAttributes);
1182 else //Add to elements
1183 elements.Add (xmlAttributes);
1185 // OtherWise add in the Hashtable.
1186 memberTable.Add (member.Name, xmlAttributes);
1188 if (xmlAttributes.XmlAnyAttribute != null || xmlAttributes.XmlText != null)
1191 if (xmlAttributes.XmlElements.Count > 0) {
1192 foreach (XmlElementAttribute elem in xmlAttributes.XmlElements) {
1193 if (elem.Type != null)
1194 FillTypeTable (elem.Type);
1196 FillTypeTable (propertyInfo.PropertyType);
1201 if (!IsInbuiltType (propertyInfo.PropertyType))
1202 FillTypeTable (propertyInfo.PropertyType);
1206 // Sort the attributes for the members according to their Order
1207 // This is an extension to MS's Implementation and will be useful
1208 // if our reflection does not return the same order of elements
1211 BubbleSort (elements, XmlAttributes.attrComparer);
1214 private void FillArrayType (Type type)
1216 if (type.GetArrayRank () != 1)
1217 throw new Exception ("MultiDimensional Arrays are not Supported");
1219 Type arrayType = type.GetElementType ();
1221 if (arrayType.IsArray)
1222 FillArrayType (arrayType);
1223 else if (!IsInbuiltType (arrayType))
1224 FillTypeTable (arrayType);
1227 private void FillICollectionType (Type type)
1229 //Must have an public Indexer that takes an integer and
1230 //a public Count Property which returns an int.
1232 BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
1234 //Find a non indexer Count Property with return type of int
1235 PropertyInfo countProp = type.GetProperty ("Count", flags, null, typeof (int), new Type[0], null);
1236 if (countProp == null || !countProp.CanRead)
1237 throw new Exception ("Cannot Serialize " + type + " because it implements ICollectoion, but does not implement public Count property");
1238 //Find a indexer Item Property which takes an int
1239 PropertyInfo itemProp = type.GetProperty ("Item", flags, null, null, new Type[1] {typeof (int)}, null);
1240 if (itemProp == null || !itemProp.CanRead || !itemProp.CanWrite)
1241 throw new Exception ("Cannot Serialize " + type + " because it does not have a read/write indexer property that takes an int as argument");
1242 if (HasDefaultConstructor (itemProp.PropertyType))
1243 FillTypeTable (itemProp.PropertyType);
1245 XmlAttributes typeAtts = new XmlAttributes (type);
1246 ArrayList includes = typeAtts.XmlIncludes;
1247 if (includes != null) {
1248 foreach (XmlIncludeAttribute i in includes)
1249 FillTypeTable (i.Type);
1254 private void FillIEnumerableType (Type type)
1256 //Must implement a public Add method that takes a single parameter.
1257 //The Add method's parameter must be of the same type as is returned from
1258 //the Current property on the value returned from GetEnumerator, or one of that type's bases.
1260 // We currently ignore enumerable types anyway, so this method was junked.
1261 // The code did not do what the documentation above says (if that is even possible!)
1265 private void FillEnum (Type type)
1267 Hashtable memberTable = new Hashtable ();
1268 TypeTableEntry entry = new TypeTableEntry (memberTable, null, null, null);
1269 BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
1270 if (typeTable [type] == null)
1271 typeTable.Add (type, entry);
1272 string[] names = Enum.GetNames (type);
1274 foreach (string name in names) {
1275 MemberInfo[] members = type.GetMember (name);
1276 if (members.Length != 1)
1277 throw new Exception("Should never happen. Enum member not present or more than one. " + name);
1278 XmlAttributes xmlAttributes = new XmlAttributes (members[0]);
1280 if (xmlAttributes.XmlIgnore)
1283 if (xmlAttributes.XmlEnum != null)
1284 memberTable.Add (members[0].Name, xmlAttributes.XmlEnum.Name);
1286 memberTable.Add (members[0].Name, members[0].Name);
1290 private bool HasDefaultConstructor (Type type)
1292 ConstructorInfo defaultConstructor = type.GetConstructor (new Type[0]);
1293 if (defaultConstructor == null || defaultConstructor.IsAbstract || defaultConstructor.IsStatic || !defaultConstructor.IsPublic)
1299 private bool IsInbuiltType (Type type)
1303 if (/* type.IsValueType || */type == typeof (string) || type.IsPrimitive)
1305 if (type == typeof (DateTime) ||
1306 type == typeof (Guid) ||
1307 type == typeof (XmlQualifiedName) ||
1308 type == typeof (XmlNode) ||
1309 type.IsSubclassOf (typeof (XmlNode)))
1315 private static MemberInfo[] GetGraph(Type type)
1317 ArrayList typeGraph = new ArrayList ();
1318 GetGraph (type, typeGraph);
1319 return (MemberInfo[]) typeGraph.ToArray (typeof (MemberInfo));
1322 private static void GetGraph (Type type, ArrayList typeGraph)
1324 BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
1325 if (type.BaseType == null)
1327 GetGraph (type.BaseType, typeGraph);
1329 typeGraph.AddRange (type.GetFields (flags));
1330 typeGraph.AddRange (type.GetProperties (flags));
1333 private string GetXmlValue (object value)
1340 Type type = value.GetType ();
1342 TypeTableEntry entry = typeTable [type];
1343 if (entry != null) {
1344 Hashtable memberTable = entry.MemberTable;
1345 if (type.IsDefined (typeof (FlagsAttribute), false)) {
1346 //If value is exactly a single enum member
1347 if (memberTable.Contains (value.ToString ()))
1348 return (string) memberTable[value.ToString ()];
1351 int enumval = (int) value;
1352 string[] names = Enum.GetNames (type);
1354 foreach (string key in names) {
1355 if (!memberTable.ContainsKey (key))
1358 //Otherwise multiple values.
1359 int val = (int) Enum.Parse (type, key);
1360 if (val != 0 && (enumval & val) == val)
1361 retval += " " + (string) memberTable[Enum.GetName (type, val)];
1364 retval = retval.Trim ();
1366 if (retval.Length == 0)
1371 else if (memberTable.ContainsKey (value.ToString ()))
1372 return (string) memberTable[value.ToString()];
1377 throw new Exception ("Unknown Enumeration");
1380 if (value is byte[])
1381 return XmlCustomFormatter.FromByteArrayBase64((byte[])value);
1383 return XmlConvert.ToString((Guid)value);
1384 if(value is DateTime)
1385 return XmlConvert.ToString((DateTime)value);
1386 if(value is TimeSpan)
1387 return XmlConvert.ToString((TimeSpan)value);
1389 return XmlConvert.ToString((bool)value);
1391 return XmlConvert.ToString((byte)value);
1393 return XmlCustomFormatter.FromChar((char)value);
1394 if(value is decimal)
1395 return XmlConvert.ToString((decimal)value);
1397 return XmlConvert.ToString((double)value);
1399 return XmlConvert.ToString((short)value);
1401 return XmlConvert.ToString((int)value);
1403 return XmlConvert.ToString((long)value);
1405 return XmlConvert.ToString((sbyte)value);
1407 return XmlConvert.ToString((float)value);
1409 return XmlConvert.ToString((ushort)value);
1411 return XmlConvert.ToString((uint)value);
1413 return XmlConvert.ToString((ulong)value);
1414 if (value is XmlQualifiedName) {
1415 if (((XmlQualifiedName) value).IsEmpty)
1418 return (value == null) ? null : value.ToString ();
1421 [MonoTODO ("Remove FIXMEs")]
1422 private static void ProcessAttributes (XmlAttributes attrs, Hashtable memberTable)
1424 if (attrs.XmlAnyAttribute != null) {
1427 foreach (XmlAnyElementAttribute anyelem in attrs.XmlAnyElements)
1428 memberTable.Add (new XmlQualifiedName (anyelem.Name, anyelem.Namespace), attrs);
1430 if (attrs.XmlArray != null) {
1434 foreach (XmlArrayItemAttribute item in attrs.XmlArrayItems)
1435 memberTable.Add (new XmlQualifiedName (item.ElementName, item.Namespace), attrs);
1437 if (attrs.XmlAttribute != null)
1438 memberTable.Add (new XmlQualifiedName (attrs.XmlAttribute.AttributeName,attrs.XmlAttribute.Namespace), attrs);
1440 if (attrs.XmlChoiceIdentifier != null) {
1444 foreach (XmlElementAttribute elem in attrs.XmlElements)
1445 memberTable.Add (new XmlQualifiedName (elem.ElementName, elem.Namespace), attrs);
1447 if (attrs.XmlEnum != null) {
1451 if (attrs.XmlType != null)
1452 memberTable.Add (new XmlQualifiedName (attrs.XmlType.TypeName, attrs.XmlType.Namespace), attrs);
1455 private bool Implements (Type type, Type interfaceType)
1457 return (type.GetInterface (interfaceType.Name) == interfaceType);
1460 private static void BubbleSort (ArrayList array, IComparer comparer)
1462 array.Sort (comparer);
1464 int len = array.Count;
1466 for (int i=0; i < len; i++) {
1467 for (int j=0; j < len -i -1; j++) {
1470 if (comparer.Compare (obj2 , obj1 ) < 0) {
1478 #endregion // Methods