2 // KnownTypeCollection.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections;
31 using System.Collections.Generic;
32 using System.Collections.ObjectModel;
34 using System.Reflection;
36 using System.Xml.Schema;
38 using QName = System.Xml.XmlQualifiedName;
39 using System.Xml.Serialization;
41 namespace System.Runtime.Serialization
44 XmlFormatter implementation design inference:
47 - No XML Schema types are directly used. There are some maps from
48 xs:blahType to ms:blahType where the namespaceURI for prefix "ms" is
49 "http://schemas.microsoft.com/2003/10/Serialization/" .
52 - An object being serialized 1) must be of type System.Object, or
53 2) must be null, or 3) must have either a [DataContract] attribute
54 or a [Serializable] attribute to be serializable.
55 - When the object is either of type System.Object or null, then the
56 XML type is "anyType".
57 - When the object is [Serializable], then the runtime-serialization
58 compatible object graph is written.
59 - Otherwise the serialization is based on contract attributes.
60 ([Serializable] takes precedence).
63 - For type A to be serializable, the base type B of A must be
65 - If a type which is [Serializable] and whose base type has a
66 [DataContract], then for base type members [DataContract] is taken.
67 - It is vice versa i.e. if the base type is [Serializable] and the
68 derived type has a [DataContract], then [Serializable] takes place
71 known type collection:
72 - It internally manages mapping store keyed by contract QNames.
73 KnownTypeCollection.Add() checks if the same QName contract already
74 exists (and raises InvalidOperationException if required).
77 internal static class TypeExtensions
79 public static T GetCustomAttribute<T> (this MemberInfo type, bool inherit)
81 var arr = type.GetCustomAttributes (typeof (T), inherit);
82 return arr != null && arr.Length == 1 ? (T) arr [0] : default (T);
85 public static IEnumerable<Type> GetInterfacesOrSelfInterface (this Type type)
89 foreach (var t in type.GetInterfaces ())
93 public static bool ImplementsInterface (this Type type, Type iface)
95 foreach (var t in type.GetInterfacesOrSelfInterface ()) {
100 var baseType = type.BaseType;
101 if (baseType != null)
102 return baseType.ImplementsInterface (iface);
108 internal sealed class KnownTypeCollection : Collection<Type>
110 internal const string MSSimpleNamespace =
111 "http://schemas.microsoft.com/2003/10/Serialization/";
112 internal const string MSArraysNamespace =
113 "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
114 internal const string DefaultClrNamespaceBase =
115 "http://schemas.datacontract.org/2004/07/";
116 internal const string DefaultClrNamespaceSystem =
117 "http://schemas.datacontract.org/2004/07/System";
120 static QName any_type, bool_type,
121 byte_type, date_type, decimal_type, double_type,
122 float_type, string_type,
123 short_type, int_type, long_type,
124 ubyte_type, ushort_type, uint_type, ulong_type,
126 any_uri_type, base64_type, duration_type, qname_type,
127 // custom in ms nsURI schema
128 char_type, guid_type,
129 // not in ms nsURI schema
130 dbnull_type, date_time_offset_type;
132 // XmlSchemaType.GetBuiltInPrimitiveType() does not exist in moonlight, so I had to explicitly add them. And now that we have it, it does not make much sense to use #if MOONLIGHT ... #endif for XmlSchemaType anymore :-(
133 static Dictionary<string,Type> xs_predefined_types = new Dictionary<string,Type> ();
135 static KnownTypeCollection ()
137 string s = MSSimpleNamespace;
138 any_type = new QName ("anyType", s);
139 any_uri_type = new QName ("anyURI", s);
140 bool_type = new QName ("boolean", s);
141 base64_type = new QName ("base64Binary", s);
142 date_type = new QName ("dateTime", s);
143 duration_type = new QName ("duration", s);
144 qname_type = new QName ("QName", s);
145 decimal_type = new QName ("decimal", s);
146 double_type = new QName ("double", s);
147 float_type = new QName ("float", s);
148 byte_type = new QName ("byte", s);
149 short_type = new QName ("short", s);
150 int_type = new QName ("int", s);
151 long_type = new QName ("long", s);
152 ubyte_type = new QName ("unsignedByte", s);
153 ushort_type = new QName ("unsignedShort", s);
154 uint_type = new QName ("unsignedInt", s);
155 ulong_type = new QName ("unsignedLong", s);
156 string_type = new QName ("string", s);
157 guid_type = new QName ("guid", s);
158 char_type = new QName ("char", s);
160 dbnull_type = new QName ("DBNull", DefaultClrNamespaceBase + "System");
161 date_time_offset_type = new QName ("DateTimeOffset", DefaultClrNamespaceBase + "System");
163 xs_predefined_types.Add ("string", typeof (string));
164 xs_predefined_types.Add ("boolean", typeof (bool));
165 xs_predefined_types.Add ("float", typeof (float));
166 xs_predefined_types.Add ("double", typeof (double));
167 xs_predefined_types.Add ("decimal", typeof (decimal));
168 xs_predefined_types.Add ("duration", typeof (TimeSpan));
169 xs_predefined_types.Add ("dateTime", typeof (DateTime));
170 xs_predefined_types.Add ("date", typeof (DateTime));
171 xs_predefined_types.Add ("time", typeof (DateTime));
172 xs_predefined_types.Add ("gYearMonth", typeof (DateTime));
173 xs_predefined_types.Add ("gYear", typeof (DateTime));
174 xs_predefined_types.Add ("gMonthDay", typeof (DateTime));
175 xs_predefined_types.Add ("gDay", typeof (DateTime));
176 xs_predefined_types.Add ("gMonth", typeof (DateTime));
177 xs_predefined_types.Add ("hexBinary", typeof (byte []));
178 xs_predefined_types.Add ("base64Binary", typeof (byte []));
179 xs_predefined_types.Add ("anyURI", typeof (Uri));
180 xs_predefined_types.Add ("QName", typeof (QName));
181 xs_predefined_types.Add ("NOTATION", typeof (string));
183 xs_predefined_types.Add ("normalizedString", typeof (string));
184 xs_predefined_types.Add ("token", typeof (string));
185 xs_predefined_types.Add ("language", typeof (string));
186 xs_predefined_types.Add ("IDREFS", typeof (string []));
187 xs_predefined_types.Add ("ENTITIES", typeof (string []));
188 xs_predefined_types.Add ("NMTOKEN", typeof (string));
189 xs_predefined_types.Add ("NMTOKENS", typeof (string []));
190 xs_predefined_types.Add ("Name", typeof (string));
191 xs_predefined_types.Add ("NCName", typeof (string));
192 xs_predefined_types.Add ("ID", typeof (string));
193 xs_predefined_types.Add ("IDREF", typeof (string));
194 xs_predefined_types.Add ("ENTITY", typeof (string));
196 xs_predefined_types.Add ("integer", typeof (decimal));
197 xs_predefined_types.Add ("nonPositiveInteger", typeof (int));
198 xs_predefined_types.Add ("negativeInteger", typeof (int));
199 xs_predefined_types.Add ("long", typeof (long));
200 xs_predefined_types.Add ("int", typeof (int));
201 xs_predefined_types.Add ("short", typeof (short));
202 xs_predefined_types.Add ("byte", typeof (sbyte));
203 xs_predefined_types.Add ("nonNegativeInteger", typeof (decimal));
204 xs_predefined_types.Add ("unsignedLong", typeof (ulong));
205 xs_predefined_types.Add ("unsignedInt", typeof (uint));
206 xs_predefined_types.Add ("unsignedShort", typeof (ushort));
207 xs_predefined_types.Add ("unsignedByte", typeof (byte));
208 xs_predefined_types.Add ("positiveInteger", typeof (decimal));
210 xs_predefined_types.Add ("anyType", typeof (object));
213 // FIXME: find out how QName and guid are processed
215 internal QName GetXmlName (Type type)
217 SerializationMap map = FindUserMap (type);
220 return GetPredefinedTypeName (type);
223 internal static QName GetPredefinedTypeName (Type type)
225 QName name = GetPrimitiveTypeName (type);
226 if (name != QName.Empty)
228 if (type == typeof (DBNull))
233 internal static QName GetPrimitiveTypeName (Type type)
235 if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
236 return GetPrimitiveTypeName (type.GetGenericArguments () [0]);
241 switch (Type.GetTypeCode (type)) {
242 case TypeCode.Object: // other than System.Object
243 case TypeCode.DBNull: // it is natively mapped, but not in ms serialization namespace.
246 if (type == typeof (object))
248 if (type == typeof (Guid))
250 if (type == typeof (TimeSpan))
251 return duration_type;
252 if (type == typeof (byte []))
254 if (type == typeof (Uri))
256 if (type == typeof (DateTimeOffset))
257 return date_time_offset_type;
259 case TypeCode.Boolean:
265 case TypeCode.DateTime:
267 case TypeCode.Decimal:
269 case TypeCode.Double:
279 case TypeCode.Single:
281 case TypeCode.String:
283 case TypeCode.UInt16:
285 case TypeCode.UInt32:
287 case TypeCode.UInt64:
292 internal static string PredefinedTypeObjectToString (object obj)
294 Type type = obj.GetType ();
295 switch (Type.GetTypeCode (type)) {
296 case TypeCode.Object: // other than System.Object
299 if (type == typeof (object))
301 if (type == typeof (Guid))
302 return XmlConvert.ToString ((Guid) obj);
303 if (type == typeof (TimeSpan))
304 return XmlConvert.ToString ((TimeSpan) obj);
305 if (type == typeof (byte []))
306 return Convert.ToBase64String ((byte []) obj);
307 if (type == typeof (Uri))
308 return ((Uri) obj).ToString ();
309 throw new Exception ("Internal error: missing predefined type serialization for type " + type.FullName);
310 case TypeCode.DBNull: // predefined, but not primitive
312 case TypeCode.Boolean:
313 return XmlConvert.ToString ((bool) obj);
315 return XmlConvert.ToString ((int)((byte) obj));
317 return XmlConvert.ToString ((uint) (char) obj);
318 case TypeCode.DateTime:
319 return XmlConvert.ToString ((DateTime) obj, XmlDateTimeSerializationMode.RoundtripKind);
320 case TypeCode.Decimal:
321 return XmlConvert.ToString ((decimal) obj);
322 case TypeCode.Double:
323 return XmlConvert.ToString ((double) obj);
325 return XmlConvert.ToString ((short) obj);
327 return XmlConvert.ToString ((int) obj);
329 return XmlConvert.ToString ((long) obj);
331 return XmlConvert.ToString ((sbyte) obj);
332 case TypeCode.Single:
333 return XmlConvert.ToString ((float) obj);
334 case TypeCode.String:
336 case TypeCode.UInt16:
337 return XmlConvert.ToString ((int) (ushort) obj);
338 case TypeCode.UInt32:
339 return XmlConvert.ToString ((uint) obj);
340 case TypeCode.UInt64:
341 return XmlConvert.ToString ((ulong) obj);
345 internal static Type GetPrimitiveTypeFromName (QName name)
347 switch (name.Namespace) {
348 case DefaultClrNamespaceSystem:
351 return typeof (DBNull);
352 case "DateTimeOffset":
353 return typeof (DateTimeOffset);
356 case XmlSchema.Namespace:
357 return xs_predefined_types.FirstOrDefault (p => p.Key == name.Name).Value;
358 case MSSimpleNamespace:
363 return typeof (bool);
365 return typeof (byte []);
367 return typeof (DateTime);
369 return typeof (TimeSpan);
371 return typeof (QName);
373 return typeof (decimal);
375 return typeof (double);
377 return typeof (float);
379 return typeof (sbyte);
381 return typeof (short);
385 return typeof (long);
387 return typeof (byte);
388 case "unsignedShort":
389 return typeof (ushort);
391 return typeof (uint);
393 return typeof (ulong);
395 return typeof (string);
397 return typeof (object);
399 return typeof (Guid);
401 return typeof (char);
409 internal static object PredefinedTypeStringToObject (string s,
410 string name, XmlReader reader)
414 return new Uri(s,UriKind.RelativeOrAbsolute);
416 return XmlConvert.ToBoolean (s);
418 return Convert.FromBase64String (s);
420 return XmlConvert.ToDateTime (s, XmlDateTimeSerializationMode.RoundtripKind);
422 return XmlConvert.ToTimeSpan (s);
424 int idx = s.IndexOf (':');
425 string l = idx < 0 ? s : s.Substring (idx + 1);
426 return idx < 0 ? new QName (l) :
427 new QName (l, reader.LookupNamespace (
428 s.Substring (0, idx)));
430 return XmlConvert.ToDecimal (s);
432 return XmlConvert.ToDouble (s);
434 return XmlConvert.ToSingle (s);
436 return XmlConvert.ToSByte (s);
438 return XmlConvert.ToInt16 (s);
440 return XmlConvert.ToInt32 (s);
442 return XmlConvert.ToInt64 (s);
444 return XmlConvert.ToByte (s);
445 case "unsignedShort":
446 return XmlConvert.ToUInt16 (s);
448 return XmlConvert.ToUInt32 (s);
450 return XmlConvert.ToUInt64 (s);
454 return XmlConvert.ToGuid (s);
458 return (char) XmlConvert.ToUInt32 (s);
460 throw new Exception ("Unanticipated primitive type: " + name);
464 List<SerializationMap> contracts = new List<SerializationMap> ();
466 public KnownTypeCollection ()
470 protected override void ClearItems ()
475 protected override void InsertItem (int index, Type type)
477 if (ShouldNotRegister (type))
479 if (!Contains (type)) {
481 base.InsertItem (index, type);
485 // FIXME: it could remove other types' dependencies.
486 protected override void RemoveItem (int index)
489 DoRemoveItem (index);
492 void DoRemoveItem (int index)
494 Type t = base [index];
495 List<SerializationMap> l = new List<SerializationMap> ();
496 foreach (SerializationMap m in contracts) {
497 if (m.RuntimeType == t)
500 foreach (SerializationMap m in l) {
501 contracts.Remove (m);
502 base.RemoveItem (index);
506 protected override void SetItem (int index, Type type)
508 if (ShouldNotRegister (type))
511 // Since this collection is not assured to be ordered, it ignores the whole Set operation if the type already exists.
517 if (TryRegister (type))
518 base.InsertItem (index - 1, type);
521 internal SerializationMap FindUserMap (Type type)
524 for (int i = 0; i < contracts.Count; i++)
525 if (type == contracts [i].RuntimeType)
526 return contracts [i];
531 internal SerializationMap FindUserMap (QName qname)
534 return contracts.FirstOrDefault (c => c.XmlName == qname);
537 internal SerializationMap FindUserMap (QName qname, Type type)
540 return contracts.FirstOrDefault (c => c.XmlName == qname && c.RuntimeType == type);
543 internal Type GetSerializedType (Type type)
545 if (IsPrimitiveNotEnum (type))
547 Type element = GetCollectionElementType (type);
550 QName name = GetQName (type);
551 var map = FindUserMap (name, type);
553 return map.RuntimeType;
557 internal QName GetQName (Type type)
559 SerializationMap map = FindUserMap (type);
563 return GetStaticQName (type);
566 public static QName GetStaticQName (Type type)
568 if (IsPrimitiveNotEnum (type))
569 return GetPrimitiveTypeName (type);
572 return GetEnumQName (type);
574 QName qname = GetContractQName (type);
578 if (type.GetInterface ("System.Xml.Serialization.IXmlSerializable") != null)
579 //FIXME: Reusing GetSerializableQName here, since we just
580 //need name of the type..
581 return GetSerializableQName (type);
583 qname = GetCollectionContractQName (type);
587 Type element = GetCollectionElementType (type);
588 if (element != null) {
589 if (type.IsInterface || IsCustomCollectionType (type, element))
590 return GetCollectionQName (element);
593 if (GetAttribute<SerializableAttribute> (type) != null)
594 return GetSerializableQName (type);
596 // default type map - still uses GetContractQName().
597 return GetContractQName (type, null, null);
600 internal static QName GetContractQName (Type type)
602 var a = GetAttribute<DataContractAttribute> (type);
603 return a == null ? null : GetContractQName (type, a.Name, a.Namespace);
606 static QName GetCollectionContractQName (Type type)
608 var a = GetAttribute<CollectionDataContractAttribute> (type);
609 return a == null ? null : GetContractQName (type, a.Name, a.Namespace);
612 static QName GetContractQName (Type type, string name, string ns)
615 name = GetDefaultName (type);
616 else if (type.IsGenericType) {
617 var args = type.GetGenericArguments ();
618 for (int i = 0; i < args.Length; i++)
619 name = name.Replace ("{" + i + "}", GetStaticQName (args [i]).Name);
623 ns = GetDefaultNamespace (type);
624 return new QName (name, ns);
627 static QName GetEnumQName (Type type)
629 string name = null, ns = null;
634 var dca = GetAttribute<DataContractAttribute> (type);
642 ns = GetDefaultNamespace (type);
645 name = type.Namespace == null ? type.Name : type.FullName.Substring (type.Namespace.Length + 1).Replace ('+', '.');
647 return new QName (name, ns);
650 internal static string GetDefaultName (Type type)
652 // FIXME: there could be decent ways to get
653 // the same result...
654 string name = type.Namespace == null || type.Namespace.Length == 0 ? type.Name : type.FullName.Substring (type.Namespace.Length + 1).Replace ('+', '.');
655 if (type.IsGenericType) {
656 name = name.Substring (0, name.IndexOf ('`')) + "Of";
657 foreach (var t in type.GetGenericArguments ())
658 name += t.Name; // FIXME: check namespaces too
663 internal static string GetDefaultNamespace (Type type)
665 foreach (ContractNamespaceAttribute a in type.Assembly.GetCustomAttributes (typeof (ContractNamespaceAttribute), true))
666 if (a.ClrNamespace == type.Namespace)
667 return a.ContractNamespace;
668 return DefaultClrNamespaceBase + type.Namespace;
671 static QName GetCollectionQName (Type element)
673 QName eqname = GetStaticQName (element);
675 string ns = eqname.Namespace;
676 if (eqname.Namespace == MSSimpleNamespace)
677 //Arrays of Primitive types
678 ns = MSArraysNamespace;
681 "ArrayOf" + XmlConvert.EncodeLocalName (eqname.Name),
685 static QName GetSerializableQName (Type type)
688 // First, check XmlSchemaProviderAttribute and try GetSchema() to see if it returns a schema in the expected format.
689 var xpa = type.GetCustomAttribute<XmlSchemaProviderAttribute> (true);
691 var mi = type.GetMethod (xpa.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
694 var xss = new XmlSchemaSet ();
695 return (XmlQualifiedName) mi.Invoke (null, new object [] {xss});
703 string xmlName = type.Name;
704 if (type.IsGenericType) {
705 xmlName = xmlName.Substring (0, xmlName.IndexOf ('`')) + "Of";
706 foreach (var t in type.GetGenericArguments ())
707 xmlName += GetStaticQName (t).Name; // FIXME: check namespaces too
709 string xmlNamespace = GetDefaultNamespace (type);
710 var x = GetAttribute<XmlRootAttribute> (type);
712 xmlName = x.ElementName;
713 xmlNamespace = x.Namespace;
715 return new QName (XmlConvert.EncodeLocalName (xmlName), xmlNamespace);
718 static bool IsPrimitiveNotEnum (Type type)
722 if (Type.GetTypeCode (type) != TypeCode.Object) // explicitly primitive
724 if (type == typeof (Guid) || type == typeof (object) || type == typeof(TimeSpan) || type == typeof(byte[]) || type == typeof(Uri) || type == typeof(DateTimeOffset)) // special primitives
728 if (type == typeof (XmlElement) || type == typeof (XmlNode []))
732 if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
733 return IsPrimitiveNotEnum (type.GetGenericArguments () [0]);
737 bool ShouldNotRegister (Type type)
739 return IsPrimitiveNotEnum (type);
742 internal bool TryRegister (Type type)
745 return DoTryRegister (type);
749 bool DoTryRegister (Type type)
751 // exclude predefined maps
752 if (ShouldNotRegister (type))
755 if (FindUserMap (type) != null)
758 if (RegisterEnum (type) != null)
761 if (RegisterDictionary (type) != null)
764 if (RegisterCollectionContract (type) != null)
767 if (RegisterContract (type) != null)
770 if (RegisterIXmlSerializable (type) != null)
773 if (RegisterCollection (type) != null)
776 if (GetAttribute<SerializableAttribute> (type) != null) {
777 RegisterSerializable (type);
781 RegisterDefaultTypeMap (type);
785 static Type GetCollectionElementType (Type type)
788 return type.GetElementType ();
789 var ifaces = type.GetInterfacesOrSelfInterface ();
790 foreach (Type i in ifaces)
791 if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (IEnumerable<>)))
792 return i.GetGenericArguments () [0];
793 foreach (Type i in ifaces)
794 if (i == typeof (IEnumerable))
795 return typeof (object);
799 internal static T GetAttribute<T> (ICustomAttributeProvider ap) where T : Attribute
801 object [] atts = ap.GetCustomAttributes (typeof (T), false);
802 return atts.Length == 0 ? null : (T) atts [0];
805 private CollectionContractTypeMap RegisterCollectionContract (Type type)
807 var cdca = GetAttribute<CollectionDataContractAttribute> (type);
811 Type element = GetCollectionElementType (type);
813 throw new InvalidDataContractException (String.Format ("Type '{0}' is marked as collection contract, but it is not a collection", type));
814 if (type.GetMethod ("Add", new Type[] { element }) == null)
815 throw new InvalidDataContractException (String.Format ("Type '{0}' is marked as collection contract, but missing a public \"Add\" method", type));
817 TryRegister (element); // must be registered before the name conflict check.
819 QName qname = GetCollectionContractQName (type);
820 CheckStandardQName (qname);
821 var map = FindUserMap (qname, type);
823 var cmap = map as CollectionContractTypeMap;
824 if (cmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
825 throw new InvalidOperationException (String.Format ("Failed to add type {0} to known type collection. There already is a registered type for XML name {1}", type, qname));
828 var ret = new CollectionContractTypeMap (type, cdca, element, qname, this);
833 private CollectionTypeMap RegisterCollection (Type type)
835 Type element = GetCollectionElementType (type);
839 TryRegister (element);
842 * To qualify as a custom collection type, a type must have
843 * a public parameterless constructor and an "Add" method
844 * with the correct parameter type in addition to implementing
845 * one of the collection interfaces.
849 if (!type.IsArray && type.IsClass && !IsCustomCollectionType (type, element))
852 QName qname = GetCollectionQName (element);
854 var map = FindUserMap (qname, element);
856 var cmap = map as CollectionTypeMap;
857 if (cmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
858 throw new InvalidOperationException (String.Format ("Failed to add type {0} to known type collection. There already is a registered type for XML name {1}", type, qname));
862 CollectionTypeMap ret =
863 new CollectionTypeMap (type, element, qname, this);
868 static bool IsCustomCollectionType (Type type, Type elementType)
872 if (type.GetConstructor (new Type [0]) == null)
874 if (type.GetMethod ("Add", new Type[] { elementType }) == null)
880 internal static bool IsInterchangeableCollectionType (Type contractType, Type graphType,
881 out QName collectionQName)
883 collectionQName = null;
884 if (GetAttribute<CollectionDataContractAttribute> (contractType) != null)
887 var type = contractType;
888 if (type.IsGenericType)
889 type = type.GetGenericTypeDefinition ();
891 var elementType = GetCollectionElementType (contractType);
892 if (elementType == null)
895 if (contractType.IsArray) {
896 if (!graphType.IsArray || !elementType.Equals (graphType.GetElementType ()))
897 throw new InvalidCastException (String.Format ("Type '{0}' cannot be converted into '{1}'.", graphType.GetElementType (), elementType));
898 } else if (!contractType.IsInterface) {
899 if (GetAttribute<SerializableAttribute> (contractType) == null)
902 var graphElementType = GetCollectionElementType (graphType);
903 if (elementType != graphElementType)
906 if (!IsCustomCollectionType (contractType, elementType))
908 } else if (type.Equals (typeof (IEnumerable)) || type.Equals (typeof (IList)) ||
909 type.Equals (typeof (ICollection))) {
910 if (!graphType.ImplementsInterface (contractType))
912 } else if (type.Equals (typeof (IEnumerable<>)) || type.Equals (typeof (IList<>)) ||
913 type.Equals (typeof (ICollection<>))) {
914 var graphElementType = GetCollectionElementType (graphType);
915 if (graphElementType != elementType)
916 throw new InvalidCastException (String.Format (
917 "Cannot convert type '{0}' into '{1}'.", graphType, contractType));
919 if (!graphType.ImplementsInterface (contractType))
925 collectionQName = GetCollectionQName (elementType);
929 static bool ImplementsInterface (Type type, Type iface)
931 foreach (var i in type.GetInterfacesOrSelfInterface ())
939 static bool TypeImplementsIEnumerable (Type type)
941 foreach (var iface in type.GetInterfacesOrSelfInterface ())
942 if (iface == typeof (IEnumerable) || (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IEnumerable<>)))
948 static bool TypeImplementsIDictionary (Type type)
950 foreach (var iface in type.GetInterfacesOrSelfInterface ())
951 if (iface == typeof (IDictionary) || (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>)))
957 // it also supports contract-based dictionary.
958 private DictionaryTypeMap RegisterDictionary (Type type)
960 if (!TypeImplementsIDictionary (type))
963 var cdca = GetAttribute<CollectionDataContractAttribute> (type);
965 DictionaryTypeMap ret =
966 new DictionaryTypeMap (type, cdca, this);
968 TryRegister (ret.KeyType);
969 TryRegister (ret.ValueType);
971 var map = FindUserMap (ret.XmlName, type);
973 var dmap = map as DictionaryTypeMap;
974 if (dmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
975 throw new InvalidOperationException (String.Format ("Failed to add type {0} to known type collection. There already is a registered type for XML name {1}", type, ret.XmlName));
982 private SerializationMap RegisterSerializable (Type type)
984 QName qname = GetSerializableQName (type);
986 if (FindUserMap (qname, type) != null)
987 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
989 SharedTypeMap ret = new SharedTypeMap (type, qname, this);
995 private SerializationMap RegisterIXmlSerializable (Type type)
997 if (type.GetInterface ("System.Xml.Serialization.IXmlSerializable") == null)
1000 QName qname = GetSerializableQName (type);
1002 if (FindUserMap (qname, type) != null)
1003 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
1005 XmlSerializableMap ret = new XmlSerializableMap (type, qname, this);
1006 contracts.Add (ret);
1011 void CheckStandardQName (QName qname)
1013 switch (qname.Namespace) {
1014 case XmlSchema.Namespace:
1015 case XmlSchema.InstanceNamespace:
1016 case MSSimpleNamespace:
1017 case MSArraysNamespace:
1018 throw new InvalidOperationException (String.Format ("Namespace {0} is reserved and cannot be used for user serialization", qname.Namespace));
1023 private SharedContractMap RegisterContract (Type type)
1025 QName qname = GetContractQName (type);
1028 CheckStandardQName (qname);
1029 if (FindUserMap (qname, type) != null)
1030 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
1032 SharedContractMap ret = new SharedContractMap (type, qname, this);
1033 contracts.Add (ret);
1036 if (type.BaseType != typeof (object)) {
1037 TryRegister (type.BaseType);
1038 if (!FindUserMap (type.BaseType).IsContractAllowedType)
1039 throw new InvalidDataContractException (String.Format ("To be serializable by data contract, type '{0}' cannot inherit from non-contract and non-Serializable type '{1}'", type, type.BaseType));
1042 object [] attrs = type.GetCustomAttributes (typeof (KnownTypeAttribute), true);
1043 for (int i = 0; i < attrs.Length; i++) {
1044 KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
1045 foreach (var t in kt.GetTypes (type))
1052 DefaultTypeMap RegisterDefaultTypeMap (Type type)
1054 DefaultTypeMap ret = new DefaultTypeMap (type, this);
1055 contracts.Add (ret);
1060 private EnumMap RegisterEnum (Type type)
1062 QName qname = GetEnumQName (type);
1066 if (FindUserMap (qname, type) != null)
1067 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
1070 new EnumMap (type, qname, this);
1071 contracts.Add (ret);