5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Ankit Jain <JAnkit@novell.com>
7 // Duncan Mak (duncan@ximian.com)
8 // Eyal Alaluf (eyala@mainsoft.com)
10 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
11 // Copyright (C) 2006 Novell, Inc. http://www.novell.com
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Collections.ObjectModel;
38 using System.Reflection;
40 using System.Xml.Schema;
41 using System.Xml.Serialization;
43 using QName = System.Xml.XmlQualifiedName;
45 namespace System.Runtime.Serialization
48 XmlFormatter implementation design inference:
51 - No XML Schema types are directly used. There are some maps from
52 xs:blahType to ms:blahType where the namespaceURI for prefix "ms" is
53 "http://schemas.microsoft.com/2003/10/Serialization/" .
56 - An object being serialized 1) must be of type System.Object, or
57 2) must be null, or 3) must have either a [DataContract] attribute
58 or a [Serializable] attribute to be serializable.
59 - When the object is either of type System.Object or null, then the
60 XML type is "anyType".
61 - When the object is [Serializable], then the runtime-serialization
62 compatible object graph is written.
63 - Otherwise the serialization is based on contract attributes.
64 ([Serializable] takes precedence).
67 - For type A to be serializable, the base type B of A must be
69 - If a type which is [Serializable] and whose base type has a
70 [DataContract], then for base type members [DataContract] is taken.
71 - It is vice versa i.e. if the base type is [Serializable] and the
72 derived type has a [DataContract], then [Serializable] takes place
75 known type collection:
76 - It internally manages mapping store keyed by contract QNames.
77 KnownTypeCollection.Add() checks if the same QName contract already
78 exists (and raises InvalidOperationException if required).
81 internal abstract class SerializationMap
83 public const BindingFlags AllInstanceFlags =
84 BindingFlags.Public | BindingFlags.NonPublic |
85 BindingFlags.Instance;
87 public readonly KnownTypeCollection KnownTypes;
88 public readonly Type RuntimeType;
89 public bool IsReference; // new in 3.5 SP1
90 public List<DataMemberInfo> Members;
92 XmlSchemaSet schema_set;
96 Dictionary<Type, QName> qname_table = new Dictionary<Type, QName> ();
98 protected SerializationMap (
99 Type type, QName qname, KnownTypeCollection knownTypes)
101 KnownTypes = knownTypes;
103 if (qname.Namespace == null)
104 qname = new QName (qname.Name,
105 KnownTypeCollection.DefaultClrNamespaceBase + type.Namespace);
108 Members = new List<DataMemberInfo> ();
111 public virtual bool OutputXsiType {
115 public QName XmlName { get; set; }
117 public CollectionDataContractAttribute GetCollectionDataContractAttribute (Type type)
119 object [] atts = type.GetCustomAttributes (
120 typeof (CollectionDataContractAttribute), false);
121 return atts.Length == 0 ? null : (CollectionDataContractAttribute) atts [0];
124 public DataMemberAttribute GetDataMemberAttribute (
127 object [] atts = mi.GetCustomAttributes (
128 typeof (DataMemberAttribute), false);
129 if (atts.Length == 0)
131 return (DataMemberAttribute) atts [0];
134 bool IsPrimitive (Type type)
136 return (Type.GetTypeCode (type) != TypeCode.Object || type == typeof (object));
140 /* Returns the XmlSchemaType AND adds it to @schemas */
141 public virtual XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
143 if (IsPrimitive (RuntimeType))
146 if (generated_schema_types.ContainsKey (XmlName)) // Caching
147 return generated_schema_types [XmlName] as XmlSchemaType;
149 XmlSchemaComplexType complex_type = null;
151 complex_type = new XmlSchemaComplexType ();
152 complex_type.Name = XmlName.Name;
153 generated_schema_types [XmlName] = complex_type;
155 if (RuntimeType.BaseType == typeof (object)) {
156 complex_type.Particle = GetSequence (schemas, generated_schema_types);
158 //Has a non-System.Object base class
159 XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension ();
160 XmlSchemaComplexContent content = new XmlSchemaComplexContent ();
162 complex_type.ContentModel = content;
163 content.Content = extension;
165 KnownTypes.Add (RuntimeType.BaseType);
166 SerializationMap map = KnownTypes.FindUserMap (RuntimeType.BaseType);
167 //FIXME: map == null ?
168 map.GetSchemaType (schemas, generated_schema_types);
170 extension.Particle = GetSequence (schemas, generated_schema_types);
171 extension.BaseTypeName = GetQualifiedName (RuntimeType.BaseType);
174 XmlSchemaElement schemaElement = GetSchemaElement (XmlName, complex_type);
175 XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
176 schema.Items.Add (complex_type);
177 schema.Items.Add (schemaElement);
178 schemas.Reprocess (schema);
183 /* Returns the <xs:sequence> for the data members */
184 XmlSchemaSequence GetSequence (XmlSchemaSet schemas,
185 Dictionary<QName, XmlSchemaType> generated_schema_types)
187 List<DataMemberInfo> members = GetMembers ();
189 XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
190 XmlSchemaSequence sequence = new XmlSchemaSequence ();
191 foreach (DataMemberInfo dmi in members) {
192 // delegates are not supported.
193 if (!dmi.MemberType.IsAbstract && typeof (System.Delegate).IsAssignableFrom (dmi.MemberType))
196 XmlSchemaElement element = new XmlSchemaElement ();
197 element.Name = dmi.XmlName;
199 KnownTypes.Add (dmi.MemberType);
200 SerializationMap map = KnownTypes.FindUserMap (dmi.MemberType);
202 XmlSchemaType schema_type = map.GetSchemaType (schemas, generated_schema_types);
203 if (schema_type is XmlSchemaComplexType)
204 element.IsNillable = true;
207 if (dmi.MemberType == typeof (string))
208 element.IsNillable = true;
211 element.MinOccurs = 0;
213 element.SchemaTypeName = GetQualifiedName (dmi.MemberType);
214 AddImport (schema, element.SchemaTypeName.Namespace);
216 sequence.Items.Add (element);
219 schemas.Reprocess (schema);
223 //FIXME: Replace with a dictionary ?
224 void AddImport (XmlSchema schema, string ns)
226 if (ns == XmlSchema.Namespace || schema.TargetNamespace == ns)
229 foreach (XmlSchemaObject o in schema.Includes) {
230 XmlSchemaImport import = o as XmlSchemaImport;
233 if (import.Namespace == ns)
237 XmlSchemaImport imp = new XmlSchemaImport ();
239 schema.Includes.Add (imp);
243 //Returns list of data members for this type ONLY
244 public virtual List<DataMemberInfo> GetMembers ()
246 throw new NotImplementedException (String.Format ("Implement me for {0}", this));
250 protected XmlSchemaElement GetSchemaElement (QName qname, XmlSchemaType schemaType)
252 XmlSchemaElement schemaElement = new XmlSchemaElement ();
253 schemaElement.Name = qname.Name;
254 schemaElement.SchemaTypeName = qname;
256 if (schemaType is XmlSchemaComplexType)
257 schemaElement.IsNillable = true;
259 return schemaElement;
262 protected XmlSchema GetSchema (XmlSchemaSet schemas, string ns)
264 ICollection colln = schemas.Schemas (ns);
265 if (colln.Count > 0) {
267 throw new Exception (String.Format (
268 "More than 1 schema for namespace '{0}' found.", ns));
269 foreach (object o in colln)
271 return (o as XmlSchema);
274 XmlSchema schema = new XmlSchema ();
275 schema.TargetNamespace = ns;
276 schema.ElementFormDefault = XmlSchemaForm.Qualified;
277 schemas.Add (schema);
282 protected XmlQualifiedName GetQualifiedName (Type type)
284 if (qname_table.ContainsKey (type))
285 return qname_table [type];
287 QName qname = KnownTypes.GetQName (type);
288 if (qname.Namespace == KnownTypeCollection.MSSimpleNamespace)
289 qname = new QName (qname.Name, XmlSchema.Namespace);
291 qname_table [type] = qname;
296 public virtual void Serialize (object graph,
297 XmlFormatterSerializer serializer)
301 label = (string) serializer.References [graph];
303 serializer.Writer.WriteAttributeString ("z", "Ref", KnownTypeCollection.MSSimpleNamespace, label);
306 label = "i" + (serializer.References.Count + 1);
307 serializer.References.Add (graph, label);
309 else if (serializer.SerializingObjects.Contains (graph))
310 throw new SerializationException (String.Format ("Circular reference of an object in the object graph was found: '{0}' of type {1}", graph, graph.GetType ()));
311 serializer.SerializingObjects.Add (graph);
314 serializer.Writer.WriteAttributeString ("z", "Id", KnownTypeCollection.MSSimpleNamespace, label);
316 SerializeNonReference (graph, serializer);
318 serializer.SerializingObjects.Remove (graph);
321 public virtual void SerializeNonReference (object graph,
322 XmlFormatterSerializer serializer)
324 foreach (DataMemberInfo dmi in Members) {
325 FieldInfo fi = dmi.Member as FieldInfo;
326 PropertyInfo pi = fi == null ?
327 (PropertyInfo) dmi.Member : null;
328 Type type = fi != null ?
329 fi.FieldType : pi.PropertyType;
330 object value = fi != null ?
331 fi.GetValue (graph) :
332 pi.GetValue (graph, null);
334 serializer.WriteStartElement (dmi.XmlName, dmi.XmlRootNamespace, dmi.XmlNamespace);
335 serializer.Serialize (type, value);
336 serializer.WriteEndElement ();
340 public virtual object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
342 bool isEmpty = reader.IsEmptyElement;
343 reader.ReadStartElement ();
344 reader.MoveToContent ();
349 res = DeserializeEmptyContent (reader, deserializer);
351 res = DeserializeContent (reader, deserializer);
353 reader.MoveToContent ();
354 if (!isEmpty && reader.NodeType == XmlNodeType.EndElement)
355 reader.ReadEndElement ();
356 else if (!isEmpty && reader.NodeType != XmlNodeType.None) {
357 var li = reader as IXmlLineInfo;
358 throw new SerializationException (String.Format ("Deserializing type '{3}'. Expecting state 'EndElement'. Encountered state '{0}' with name '{1}' with namespace '{2}'.{4}",
362 RuntimeType.FullName,
363 li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : String.Empty));
368 // This is sort of hack. The argument reader already moved ahead of
369 // the actual empty element.It's just for historical consistency.
370 public virtual object DeserializeEmptyContent (XmlReader reader,
371 XmlFormatterDeserializer deserializer)
373 return DeserializeContent (reader, deserializer, true);
376 public virtual object DeserializeContent (XmlReader reader,
377 XmlFormatterDeserializer deserializer)
379 return DeserializeContent (reader, deserializer, false);
382 object DeserializeContent (XmlReader reader,
383 XmlFormatterDeserializer deserializer, bool empty)
385 object instance = FormatterServices.GetUninitializedObject (RuntimeType);
386 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
387 bool [] filled = new bool [Members.Count];
388 int memberInd = -1, ordered = -1;
389 while (!empty && reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
390 DataMemberInfo dmi = null;
392 for (; i < Members.Count; i++) { // unordered
393 if (Members [i].Order >= 0)
395 if (reader.LocalName == Members [i].XmlName &&
396 reader.NamespaceURI == Members [i].XmlRootNamespace) {
402 for (i = Math.Max (i, ordered); i < Members.Count; i++) { // ordered
405 if (reader.LocalName == Members [i].XmlName &&
406 reader.NamespaceURI == Members [i].XmlRootNamespace) {
418 SetValue (dmi, instance, deserializer.Deserialize (dmi.MemberType, reader));
419 filled [memberInd] = true;
420 reader.MoveToContent ();
422 for (int i = 0; i < Members.Count; i++)
423 if (!filled [i] && Members [i].IsRequired)
424 throw MissingRequiredMember (Members [i], reader);
429 // For now it could be private.
430 protected Exception MissingRequiredMember (DataMemberInfo dmi, XmlReader reader)
432 var li = reader as IXmlLineInfo;
433 return new ArgumentException (String.Format ("Data contract member {0} for the type {1} is required, but missing in the input XML.{2}",
434 new QName (dmi.XmlName, dmi.XmlNamespace),
436 li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : null));
439 // For now it could be private.
440 protected void SetValue (DataMemberInfo dmi, object obj, object value)
443 if (dmi.Member is PropertyInfo)
444 ((PropertyInfo) dmi.Member).SetValue (obj, value, null);
446 ((FieldInfo) dmi.Member).SetValue (obj, value);
447 } catch (Exception ex) {
448 throw new InvalidOperationException (String.Format ("Failed to set value of type {0} for property {1}", value != null ? value.GetType () : null, dmi.Member), ex);
452 protected DataMemberInfo CreateDataMemberInfo (DataMemberAttribute dma, MemberInfo mi, Type type)
454 KnownTypes.Add (type);
455 QName qname = KnownTypes.GetQName (type);
456 string rootNamespace = KnownTypes.GetQName (mi.DeclaringType).Namespace;
457 if (KnownTypeCollection.GetPrimitiveTypeFromName (qname.Name) != null)
458 return new DataMemberInfo (mi, dma, rootNamespace, null);
460 return new DataMemberInfo (mi, dma, rootNamespace, qname.Namespace);
464 internal class XmlSerializableMap : SerializationMap
466 public XmlSerializableMap (Type type, QName qname, KnownTypeCollection knownTypes)
467 : base (type, qname, knownTypes)
471 public override void Serialize (object graph, XmlFormatterSerializer serializer)
473 IXmlSerializable ixs = graph as IXmlSerializable;
475 //FIXME: Throw what exception here?
476 throw new SerializationException ();
478 ixs.WriteXml (serializer.Writer);
481 public override object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
483 IXmlSerializable ixs = (IXmlSerializable) FormatterServices.GetUninitializedObject (RuntimeType);
484 ixs.ReadXml (reader);
489 // FIXME: verify return value sanity.
490 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
497 internal class SharedContractMap : SerializationMap
499 public SharedContractMap (
500 Type type, QName qname, KnownTypeCollection knownTypes)
501 : base (type, qname, knownTypes)
505 internal void Initialize ()
507 Type baseType = RuntimeType;
508 List <DataMemberInfo> members = new List <DataMemberInfo> ();
509 object [] atts = baseType.GetCustomAttributes (
510 typeof (DataContractAttribute), false);
511 IsReference = atts.Length > 0 ? (((DataContractAttribute) atts [0]).IsReference) : false;
513 while (baseType != null) {
514 QName bqname = KnownTypes.GetQName (baseType);
516 members = GetMembers (baseType, bqname, true);
517 members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
518 Members.InsertRange (0, members);
521 baseType = baseType.BaseType;
525 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
527 List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
528 BindingFlags flags = AllInstanceFlags;
530 flags |= BindingFlags.DeclaredOnly;
532 foreach (PropertyInfo pi in type.GetProperties (flags)) {
533 DataMemberAttribute dma =
534 GetDataMemberAttribute (pi);
537 KnownTypes.TryRegister (pi.PropertyType);
538 var map = KnownTypes.FindUserMap (pi.PropertyType);
539 if (!pi.CanRead || (!pi.CanWrite && !(map is ICollectionTypeMap)))
540 throw new InvalidDataContractException (String.Format (
541 "DataMember property '{0}' on type '{1}' must have both getter and setter.", pi, pi.DeclaringType));
542 data_members.Add (CreateDataMemberInfo (dma, pi, pi.PropertyType));
545 foreach (FieldInfo fi in type.GetFields (flags)) {
546 DataMemberAttribute dma =
547 GetDataMemberAttribute (fi);
551 throw new InvalidDataContractException (String.Format (
552 "DataMember field {0} must not be read-only.", fi));
553 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
559 public override List<DataMemberInfo> GetMembers ()
565 internal class DefaultTypeMap : SerializationMap
567 public DefaultTypeMap (Type type, KnownTypeCollection knownTypes)
568 : base (type, KnownTypeCollection.GetContractQName (type, null, null), knownTypes)
570 Members.AddRange (GetDefaultMembers ());
573 List<DataMemberInfo> GetDefaultMembers ()
575 var l = new List<DataMemberInfo> ();
576 foreach (var mi in RuntimeType.GetMembers ()) {
578 FieldInfo fi = mi as FieldInfo;
579 mt = fi == null ? null : fi.FieldType;
580 PropertyInfo pi = mi as PropertyInfo;
581 if (pi != null && pi.CanRead && pi.CanWrite && pi.GetIndexParameters ().Length == 0)
582 mt = pi.PropertyType;
585 if (mi.GetCustomAttributes (typeof (IgnoreDataMemberAttribute), false).Length != 0)
587 l.Add (new DataMemberInfo (mi, new DataMemberAttribute (), null, null));
589 l.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
594 // FIXME: it still needs to consider ItemName/KeyName/ValueName
595 // (especially Dictionary collection is not likely considered yet.)
596 internal class CollectionContractTypeMap : CollectionTypeMap
598 public CollectionContractTypeMap (
599 Type type, CollectionDataContractAttribute a, Type elementType,
600 QName qname, KnownTypeCollection knownTypes)
601 : base (type, elementType, qname, knownTypes)
603 IsReference = a.IsReference;
606 internal override string CurrentNamespace {
607 get { return XmlName.Namespace; }
611 internal interface ICollectionTypeMap
615 internal class CollectionTypeMap : SerializationMap, ICollectionTypeMap
618 internal QName element_qname;
619 MethodInfo add_method;
621 public CollectionTypeMap (
622 Type type, Type elementType,
623 QName qname, KnownTypeCollection knownTypes)
624 : base (type, qname, knownTypes)
626 element_type = elementType;
627 element_qname = KnownTypes.GetQName (element_type);
628 var icoll = GetGenericCollectionInterface (RuntimeType);
630 if (RuntimeType.IsInterface) {
631 add_method = RuntimeType.GetMethod ("Add", icoll.GetGenericArguments ());
633 var imap = RuntimeType.GetInterfaceMap (icoll);
634 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
635 if (imap.InterfaceMethods [i].Name == "Add") {
636 add_method = imap.TargetMethods [i];
639 if (add_method == null)
640 add_method = type.GetMethod ("Add", icoll.GetGenericArguments ());
645 static Type GetGenericCollectionInterface (Type type)
647 foreach (var iface in type.GetInterfaces ())
648 if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (ICollection<>))
654 public override bool OutputXsiType {
655 get { return false; }
658 internal virtual string CurrentNamespace {
660 string ns = element_qname.Namespace;
661 if (ns == KnownTypeCollection.MSSimpleNamespace)
662 ns = KnownTypeCollection.MSArraysNamespace;
667 public override void SerializeNonReference (object graph,
668 XmlFormatterSerializer serializer)
671 foreach (object o in (IEnumerable) graph) {
672 serializer.WriteStartElement (element_qname.Name, XmlName.Namespace, CurrentNamespace);
673 serializer.Serialize (element_type, o);
674 serializer.WriteEndElement ();
678 object CreateInstance ()
680 if (RuntimeType.IsArray)
681 return new ArrayList ();
682 if (RuntimeType.IsInterface) {
683 var icoll = GetGenericCollectionInterface (RuntimeType);
685 return Activator.CreateInstance (typeof (List<>).MakeGenericType (RuntimeType.GetGenericArguments () [0])); // List<T>
687 return new ArrayList ();
689 #if NET_2_1 // FIXME: is it fine?
690 return Activator.CreateInstance (RuntimeType);
692 return Activator.CreateInstance (RuntimeType, true);
696 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
698 return CreateInstance ();
701 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer)
703 object instance = CreateInstance ();
704 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
705 while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
706 object elem = deserializer.Deserialize (element_type, reader);
707 if (instance is IList)
708 ((IList)instance).Add (elem);
709 else if (add_method != null)
710 add_method.Invoke (instance, new object [] {elem});
712 throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
713 reader.MoveToContent ();
715 if (RuntimeType.IsArray)
716 return ((ArrayList)instance).ToArray (element_type);
720 public override List<DataMemberInfo> GetMembers ()
722 //Shouldn't come here at all!
723 throw new NotImplementedException ();
727 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
729 if (generated_schema_types.ContainsKey (XmlName))
732 if (generated_schema_types.ContainsKey (XmlName))
733 return generated_schema_types [XmlName];
735 QName element_qname = GetQualifiedName (element_type);
737 XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
738 complex_type.Name = XmlName.Name;
740 XmlSchemaSequence sequence = new XmlSchemaSequence ();
741 XmlSchemaElement element = new XmlSchemaElement ();
743 element.MinOccurs = 0;
744 element.MaxOccursString = "unbounded";
745 element.Name = element_qname.Name;
747 KnownTypes.Add (element_type);
748 SerializationMap map = KnownTypes.FindUserMap (element_type);
749 if (map != null) {// non-primitive type
750 map.GetSchemaType (schemas, generated_schema_types);
751 element.IsNillable = true;
754 element.SchemaTypeName = element_qname;
756 sequence.Items.Add (element);
757 complex_type.Particle = sequence;
759 XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
760 schema.Items.Add (complex_type);
761 schema.Items.Add (GetSchemaElement (XmlName, complex_type));
762 schemas.Reprocess (schema);
764 generated_schema_types [XmlName] = complex_type;
771 internal class DictionaryTypeMap : SerializationMap, ICollectionTypeMap
773 Type key_type, value_type;
774 QName dict_qname, item_qname, key_qname, value_qname;
775 MethodInfo add_method;
776 CollectionDataContractAttribute a;
778 public DictionaryTypeMap (
779 Type type, CollectionDataContractAttribute a, KnownTypeCollection knownTypes)
780 : base (type, QName.Empty, knownTypes)
784 key_type = typeof (object);
785 value_type = typeof (object);
787 var idic = GetGenericDictionaryInterface (RuntimeType);
789 var imap = RuntimeType.GetInterfaceMap (idic);
790 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
791 if (imap.InterfaceMethods [i].Name == "Add") {
792 add_method = imap.TargetMethods [i];
795 var argtypes = idic.GetGenericArguments();
796 key_type = argtypes [0];
797 value_type = argtypes [1];
798 if (add_method == null)
799 add_method = type.GetMethod ("Add", argtypes);
802 XmlName = GetDictionaryQName ();
803 item_qname = GetItemQName ();
804 key_qname = GetKeyQName ();
805 value_qname = GetValueQName ();
808 static Type GetGenericDictionaryInterface (Type type)
810 foreach (var iface in type.GetInterfaces ())
811 if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>))
817 string ContractNamespace {
818 get { return a != null && !String.IsNullOrEmpty (a.Namespace) ? a.Namespace : KnownTypeCollection.MSArraysNamespace; }
821 public Type KeyType { get { return key_type; } }
822 public Type ValueType { get { return value_type; } }
824 static readonly QName kvpair_key_qname = new QName ("Key", KnownTypeCollection.MSArraysNamespace);
825 static readonly QName kvpair_value_qname = new QName ("Value", KnownTypeCollection.MSArraysNamespace);
827 internal virtual QName GetDictionaryQName ()
829 if (a != null && !String.IsNullOrEmpty (a.Name))
830 return new QName (a.Name, ContractNamespace);
831 return new QName ("ArrayOf" + GetItemQName ().Name, KnownTypeCollection.MSArraysNamespace);
834 internal virtual QName GetItemQName ()
836 if (a != null && !String.IsNullOrEmpty (a.ItemName))
837 return new QName (a.ItemName, ContractNamespace);
838 return new QName ("KeyValueOf" + KnownTypes.GetQName (key_type).Name + KnownTypes.GetQName (value_type).Name, KnownTypeCollection.MSArraysNamespace);
841 internal virtual QName GetKeyQName ()
843 if (a != null && !String.IsNullOrEmpty (a.KeyName))
844 return new QName (a.KeyName, ContractNamespace);
845 return kvpair_key_qname;
848 internal virtual QName GetValueQName ()
850 if (a != null && !String.IsNullOrEmpty (a.ValueName))
851 return new QName (a.ValueName, ContractNamespace);
852 return kvpair_value_qname;
855 internal virtual string CurrentNamespace {
857 string ns = item_qname.Namespace;
858 if (ns == KnownTypeCollection.MSSimpleNamespace)
859 ns = KnownTypeCollection.MSArraysNamespace;
865 PropertyInfo pair_key_property, pair_value_property;
867 public override void SerializeNonReference (object graph,
868 XmlFormatterSerializer serializer)
870 if (add_method != null) { // generic
871 if (pair_type == null) {
872 pair_type = typeof (KeyValuePair<,>).MakeGenericType (add_method.DeclaringType.GetGenericArguments ());
873 pair_key_property = pair_type.GetProperty ("Key");
874 pair_value_property = pair_type.GetProperty ("Value");
876 foreach (object p in (IEnumerable) graph) {
877 serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
878 serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
879 serializer.Serialize (pair_key_property.PropertyType, pair_key_property.GetValue (p, null));
880 serializer.WriteEndElement ();
881 serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
882 serializer.Serialize (pair_value_property.PropertyType, pair_value_property.GetValue (p, null));
883 serializer.WriteEndElement ();
884 serializer.WriteEndElement ();
886 } else { // non-generic
887 foreach (DictionaryEntry p in (IEnumerable) graph) {
888 serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
889 serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
890 serializer.Serialize (key_type, p.Key);
891 serializer.WriteEndElement ();
892 serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
893 serializer.Serialize (value_type, p.Value);
894 serializer.WriteEndElement ();
895 serializer.WriteEndElement ();
900 object CreateInstance ()
902 if (RuntimeType.IsInterface) {
903 if (RuntimeType.IsGenericType && Array.IndexOf (RuntimeType.GetGenericTypeDefinition ().GetInterfaces (), typeof (IDictionary<,>)) >= 0) {
904 var gargs = RuntimeType.GetGenericArguments ();
905 return Activator.CreateInstance (typeof (Dictionary<,>).MakeGenericType (gargs [0], gargs [1])); // Dictionary<T>
908 return new Hashtable ();
910 #if NET_2_1 // FIXME: is it fine?
911 return Activator.CreateInstance (RuntimeType);
913 return Activator.CreateInstance (RuntimeType, true);
917 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
919 return DeserializeContent (reader, deserializer);
922 public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer)
924 object instance = CreateInstance ();
925 int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
926 while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
927 if (reader.IsEmptyElement)
928 throw new XmlException (String.Format ("Unexpected empty element for dictionary entry: name {0}", reader.Name));
929 // FIXME: sloppy parsing
930 reader.ReadStartElement ();// item_qname.Name, item_qname.Namespace);
931 reader.MoveToContent ();
932 object key = deserializer.Deserialize (key_type, reader);
933 reader.MoveToContent ();
934 object val = deserializer.Deserialize (value_type, reader);
935 reader.ReadEndElement (); // of pair
937 if (instance is IDictionary)
938 ((IDictionary)instance).Add (key, val);
939 else if (add_method != null)
940 add_method.Invoke (instance, new object [] {key, val});
942 throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
947 public override List<DataMemberInfo> GetMembers ()
949 //Shouldn't come here at all!
950 throw new NotImplementedException ();
954 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
956 throw new NotImplementedException ();
961 internal class SharedTypeMap : SerializationMap
963 public SharedTypeMap (
964 Type type, QName qname, KnownTypeCollection knownTypes)
965 : base (type, qname, knownTypes)
967 Members = GetMembers (type, XmlName, false);
970 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
972 List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
973 BindingFlags flags = AllInstanceFlags;
975 flags |= BindingFlags.DeclaredOnly;
977 foreach (FieldInfo fi in type.GetFields (flags)) {
978 if (fi.GetCustomAttributes (
979 typeof (NonSerializedAttribute),
984 throw new InvalidDataContractException (String.Format ("DataMember field {0} must not be read-only.", fi));
985 DataMemberAttribute dma = new DataMemberAttribute ();
986 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
989 data_members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance); // alphabetic order.
994 // Does this make sense? I doubt.
995 public override List<DataMemberInfo> GetMembers ()
998 //return GetMembers (RuntimeType, XmlName, true);
1002 internal class EnumMap : SerializationMap
1004 List<EnumMemberInfo> enum_members;
1008 Type type, QName qname, KnownTypeCollection knownTypes)
1009 : base (type, qname, knownTypes)
1011 bool has_dc = false;
1012 object [] atts = RuntimeType.GetCustomAttributes (
1013 typeof (DataContractAttribute), false);
1014 if (atts.Length != 0)
1016 flag_attr = type.GetCustomAttributes (typeof (FlagsAttribute), false).Length > 0;
1018 enum_members = new List<EnumMemberInfo> ();
1019 BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
1021 foreach (FieldInfo fi in RuntimeType.GetFields (flags)) {
1022 string name = fi.Name;
1024 EnumMemberAttribute ema =
1025 GetEnumMemberAttribute (fi);
1029 if (ema.Value != null)
1033 enum_members.Add (new EnumMemberInfo (name, fi.GetValue (null)));
1037 private EnumMemberAttribute GetEnumMemberAttribute (
1040 object [] atts = mi.GetCustomAttributes (
1041 typeof (EnumMemberAttribute), false);
1042 if (atts.Length == 0)
1044 return (EnumMemberAttribute) atts [0];
1048 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
1050 if (generated_schema_types.ContainsKey (XmlName))
1051 return generated_schema_types [XmlName];
1053 XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType ();
1054 simpleType.Name = XmlName.Name;
1056 XmlSchemaSimpleTypeRestriction simpleRestriction = new XmlSchemaSimpleTypeRestriction ();
1057 simpleType.Content = simpleRestriction;
1058 simpleRestriction.BaseTypeName = new XmlQualifiedName ("string", XmlSchema.Namespace);
1060 foreach (EnumMemberInfo emi in enum_members) {
1061 XmlSchemaEnumerationFacet e = new XmlSchemaEnumerationFacet ();
1062 e.Value = emi.XmlName;
1063 simpleRestriction.Facets.Add (e);
1066 generated_schema_types [XmlName] = simpleType;
1068 XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
1069 XmlSchemaElement element = GetSchemaElement (XmlName, simpleType);
1070 element.IsNillable = true;
1072 schema.Items.Add (simpleType);
1073 schema.Items.Add (element);
1079 public override void Serialize (object graph,
1080 XmlFormatterSerializer serializer)
1082 foreach (EnumMemberInfo emi in enum_members) {
1083 if (Enum.Equals (emi.Value, graph)) {
1084 serializer.Writer.WriteString (emi.XmlName);
1089 throw new SerializationException (String.Format (
1090 "Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
1093 public override object DeserializeEmptyContent (XmlReader reader,
1094 XmlFormatterDeserializer deserializer)
1097 throw new SerializationException (String.Format ("Enum value '' is invalid for type '{0}' and cannot be deserialized.", RuntimeType));
1098 return Enum.ToObject (RuntimeType, 0);
1101 public override object DeserializeContent (XmlReader reader,
1102 XmlFormatterDeserializer deserializer)
1104 string value = reader.NodeType != XmlNodeType.Text ? String.Empty : reader.ReadContentAsString ();
1106 if (value != String.Empty) {
1107 foreach (EnumMemberInfo emi in enum_members)
1108 if (emi.XmlName == value)
1113 throw new SerializationException (String.Format ("Enum value '{0}' is invalid for type '{1}' and cannot be deserialized.", value, RuntimeType));
1114 return Enum.ToObject (RuntimeType, 0);
1118 internal struct EnumMemberInfo
1120 public readonly string XmlName;
1121 public readonly object Value;
1123 public EnumMemberInfo (string name, object value)
1130 internal class DataMemberInfo //: KeyValuePair<int, MemberInfo>
1132 public readonly int Order;
1133 public readonly bool IsRequired;
1134 public readonly string XmlName;
1135 public readonly MemberInfo Member;
1136 public readonly string XmlNamespace;
1137 public readonly string XmlRootNamespace;
1138 public readonly Type MemberType;
1140 public DataMemberInfo (MemberInfo member, DataMemberAttribute dma, string rootNamespce, string ns)
1143 throw new ArgumentNullException ("dma");
1146 IsRequired = dma.IsRequired;
1147 XmlName = dma.Name != null ? dma.Name : member.Name;
1149 XmlRootNamespace = rootNamespce;
1150 if (Member is FieldInfo)
1151 MemberType = ((FieldInfo) Member).FieldType;
1153 MemberType = ((PropertyInfo) Member).PropertyType;
1156 public class DataMemberInfoComparer : IComparer<DataMemberInfo>
1157 , IComparer // see bug #76361
1159 public static readonly DataMemberInfoComparer Instance
1160 = new DataMemberInfoComparer ();
1162 private DataMemberInfoComparer () {}
1164 public int Compare (object o1, object o2)
1166 return Compare ((DataMemberInfo) o1,
1167 (DataMemberInfo) o2);
1170 public int Compare (DataMemberInfo d1, DataMemberInfo d2)
1172 if (d1.Order == d2.Order)
1173 return String.CompareOrdinal (d1.XmlName, d2.XmlName);
1175 return d1.Order - d2.Order;