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).
78 internal static class TypeExtensions
81 public static T GetCustomAttribute<T> (this MemberInfo type, bool inherit)
83 var arr = type.GetCustomAttributes (typeof (T), inherit);
84 return arr != null && arr.Length == 1 ? (T) arr [0] : default (T);
87 public static IEnumerable<Type> GetInterfacesOrSelfInterface (this Type type)
91 foreach (var t in type.GetInterfaces ())
95 public static bool ImplementsInterface (this Type type, Type iface)
97 foreach (var t in type.GetInterfacesOrSelfInterface ()) {
102 var baseType = type.BaseType;
103 if (baseType != null)
104 return baseType.ImplementsInterface (iface);
110 internal sealed class KnownTypeCollection : Collection<Type>
112 internal const string MSSimpleNamespace =
113 "http://schemas.microsoft.com/2003/10/Serialization/";
114 internal const string MSArraysNamespace =
115 "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
116 internal const string DefaultClrNamespaceBase =
117 "http://schemas.datacontract.org/2004/07/";
118 internal const string DefaultClrNamespaceSystem =
119 "http://schemas.datacontract.org/2004/07/System";
122 static QName any_type, bool_type,
123 byte_type, date_type, decimal_type, double_type,
124 float_type, string_type,
125 short_type, int_type, long_type,
126 ubyte_type, ushort_type, uint_type, ulong_type,
128 any_uri_type, base64_type, duration_type, qname_type,
129 // custom in ms nsURI schema
130 char_type, guid_type,
131 // not in ms nsURI schema
132 dbnull_type, date_time_offset_type;
134 // 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 :-(
135 static Dictionary<string,Type> xs_predefined_types = new Dictionary<string,Type> ();
137 static KnownTypeCollection ()
139 string s = MSSimpleNamespace;
140 any_type = new QName ("anyType", s);
141 any_uri_type = new QName ("anyURI", s);
142 bool_type = new QName ("boolean", s);
143 base64_type = new QName ("base64Binary", s);
144 date_type = new QName ("dateTime", s);
145 duration_type = new QName ("duration", s);
146 qname_type = new QName ("QName", s);
147 decimal_type = new QName ("decimal", s);
148 double_type = new QName ("double", s);
149 float_type = new QName ("float", s);
150 byte_type = new QName ("byte", s);
151 short_type = new QName ("short", s);
152 int_type = new QName ("int", s);
153 long_type = new QName ("long", s);
154 ubyte_type = new QName ("unsignedByte", s);
155 ushort_type = new QName ("unsignedShort", s);
156 uint_type = new QName ("unsignedInt", s);
157 ulong_type = new QName ("unsignedLong", s);
158 string_type = new QName ("string", s);
159 guid_type = new QName ("guid", s);
160 char_type = new QName ("char", s);
162 dbnull_type = new QName ("DBNull", DefaultClrNamespaceBase + "System");
163 date_time_offset_type = new QName ("DateTimeOffset", DefaultClrNamespaceBase + "System");
165 xs_predefined_types.Add ("string", typeof (string));
166 xs_predefined_types.Add ("boolean", typeof (bool));
167 xs_predefined_types.Add ("float", typeof (float));
168 xs_predefined_types.Add ("double", typeof (double));
169 xs_predefined_types.Add ("decimal", typeof (decimal));
170 xs_predefined_types.Add ("duration", typeof (TimeSpan));
171 xs_predefined_types.Add ("dateTime", typeof (DateTime));
172 xs_predefined_types.Add ("date", typeof (DateTime));
173 xs_predefined_types.Add ("time", typeof (DateTime));
174 xs_predefined_types.Add ("gYearMonth", typeof (DateTime));
175 xs_predefined_types.Add ("gYear", typeof (DateTime));
176 xs_predefined_types.Add ("gMonthDay", typeof (DateTime));
177 xs_predefined_types.Add ("gDay", typeof (DateTime));
178 xs_predefined_types.Add ("gMonth", typeof (DateTime));
179 xs_predefined_types.Add ("hexBinary", typeof (byte []));
180 xs_predefined_types.Add ("base64Binary", typeof (byte []));
181 xs_predefined_types.Add ("anyURI", typeof (Uri));
182 xs_predefined_types.Add ("QName", typeof (QName));
183 xs_predefined_types.Add ("NOTATION", typeof (string));
185 xs_predefined_types.Add ("normalizedString", typeof (string));
186 xs_predefined_types.Add ("token", typeof (string));
187 xs_predefined_types.Add ("language", typeof (string));
188 xs_predefined_types.Add ("IDREFS", typeof (string []));
189 xs_predefined_types.Add ("ENTITIES", typeof (string []));
190 xs_predefined_types.Add ("NMTOKEN", typeof (string));
191 xs_predefined_types.Add ("NMTOKENS", typeof (string []));
192 xs_predefined_types.Add ("Name", typeof (string));
193 xs_predefined_types.Add ("NCName", typeof (string));
194 xs_predefined_types.Add ("ID", typeof (string));
195 xs_predefined_types.Add ("IDREF", typeof (string));
196 xs_predefined_types.Add ("ENTITY", typeof (string));
198 xs_predefined_types.Add ("integer", typeof (decimal));
199 xs_predefined_types.Add ("nonPositiveInteger", typeof (int));
200 xs_predefined_types.Add ("negativeInteger", typeof (int));
201 xs_predefined_types.Add ("long", typeof (long));
202 xs_predefined_types.Add ("int", typeof (int));
203 xs_predefined_types.Add ("short", typeof (short));
204 xs_predefined_types.Add ("byte", typeof (sbyte));
205 xs_predefined_types.Add ("nonNegativeInteger", typeof (decimal));
206 xs_predefined_types.Add ("unsignedLong", typeof (ulong));
207 xs_predefined_types.Add ("unsignedInt", typeof (uint));
208 xs_predefined_types.Add ("unsignedShort", typeof (ushort));
209 xs_predefined_types.Add ("unsignedByte", typeof (byte));
210 xs_predefined_types.Add ("positiveInteger", typeof (decimal));
212 xs_predefined_types.Add ("anyType", typeof (object));
215 // FIXME: find out how QName and guid are processed
217 internal QName GetXmlName (Type type)
219 SerializationMap map = FindUserMap (type);
222 return GetPredefinedTypeName (type);
225 internal static QName GetPredefinedTypeName (Type type)
227 QName name = GetPrimitiveTypeName (type);
228 if (name != QName.Empty)
230 if (type == typeof (DBNull))
235 internal static QName GetPrimitiveTypeName (Type type)
237 if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
238 return GetPrimitiveTypeName (type.GetGenericArguments () [0]);
243 switch (Type.GetTypeCode (type)) {
244 case TypeCode.Object: // other than System.Object
245 case TypeCode.DBNull: // it is natively mapped, but not in ms serialization namespace.
248 if (type == typeof (object))
250 if (type == typeof (Guid))
252 if (type == typeof (TimeSpan))
253 return duration_type;
254 if (type == typeof (byte []))
256 if (type == typeof (Uri))
258 if (type == typeof (DateTimeOffset))
259 return date_time_offset_type;
261 case TypeCode.Boolean:
267 case TypeCode.DateTime:
269 case TypeCode.Decimal:
271 case TypeCode.Double:
281 case TypeCode.Single:
283 case TypeCode.String:
285 case TypeCode.UInt16:
287 case TypeCode.UInt32:
289 case TypeCode.UInt64:
294 internal static string PredefinedTypeObjectToString (object obj)
296 Type type = obj.GetType ();
297 switch (Type.GetTypeCode (type)) {
298 case TypeCode.Object: // other than System.Object
301 if (type == typeof (object))
303 if (type == typeof (Guid))
304 return XmlConvert.ToString ((Guid) obj);
305 if (type == typeof (TimeSpan))
306 return XmlConvert.ToString ((TimeSpan) obj);
307 if (type == typeof (byte []))
308 return Convert.ToBase64String ((byte []) obj);
309 if (type == typeof (Uri))
310 return ((Uri) obj).ToString ();
311 throw new Exception ("Internal error: missing predefined type serialization for type " + type.FullName);
312 case TypeCode.DBNull: // predefined, but not primitive
314 case TypeCode.Boolean:
315 return XmlConvert.ToString ((bool) obj);
317 return XmlConvert.ToString ((int)((byte) obj));
319 return XmlConvert.ToString ((uint) (char) obj);
320 case TypeCode.DateTime:
321 return XmlConvert.ToString ((DateTime) obj, XmlDateTimeSerializationMode.RoundtripKind);
322 case TypeCode.Decimal:
323 return XmlConvert.ToString ((decimal) obj);
324 case TypeCode.Double:
325 return XmlConvert.ToString ((double) obj);
327 return XmlConvert.ToString ((short) obj);
329 return XmlConvert.ToString ((int) obj);
331 return XmlConvert.ToString ((long) obj);
333 return XmlConvert.ToString ((sbyte) obj);
334 case TypeCode.Single:
335 return XmlConvert.ToString ((float) obj);
336 case TypeCode.String:
338 case TypeCode.UInt16:
339 return XmlConvert.ToString ((int) (ushort) obj);
340 case TypeCode.UInt32:
341 return XmlConvert.ToString ((uint) obj);
342 case TypeCode.UInt64:
343 return XmlConvert.ToString ((ulong) obj);
347 internal static Type GetPrimitiveTypeFromName (QName name)
349 switch (name.Namespace) {
350 case DefaultClrNamespaceSystem:
353 return typeof (DBNull);
354 case "DateTimeOffset":
355 return typeof (DateTimeOffset);
358 case XmlSchema.Namespace:
359 return xs_predefined_types.FirstOrDefault (p => p.Key == name.Name).Value;
360 case MSSimpleNamespace:
365 return typeof (bool);
367 return typeof (byte []);
369 return typeof (DateTime);
371 return typeof (TimeSpan);
373 return typeof (QName);
375 return typeof (decimal);
377 return typeof (double);
379 return typeof (float);
381 return typeof (sbyte);
383 return typeof (short);
387 return typeof (long);
389 return typeof (byte);
390 case "unsignedShort":
391 return typeof (ushort);
393 return typeof (uint);
395 return typeof (ulong);
397 return typeof (string);
399 return typeof (object);
401 return typeof (Guid);
403 return typeof (char);
411 internal static object PredefinedTypeStringToObject (string s,
412 string name, XmlReader reader)
416 return new Uri(s,UriKind.RelativeOrAbsolute);
418 return XmlConvert.ToBoolean (s);
420 return Convert.FromBase64String (s);
422 return XmlConvert.ToDateTime (s, XmlDateTimeSerializationMode.RoundtripKind);
424 return XmlConvert.ToTimeSpan (s);
426 int idx = s.IndexOf (':');
427 string l = idx < 0 ? s : s.Substring (idx + 1);
428 return idx < 0 ? new QName (l) :
429 new QName (l, reader.LookupNamespace (
430 s.Substring (0, idx)));
432 return XmlConvert.ToDecimal (s);
434 return XmlConvert.ToDouble (s);
436 return XmlConvert.ToSingle (s);
438 return XmlConvert.ToSByte (s);
440 return XmlConvert.ToInt16 (s);
442 return XmlConvert.ToInt32 (s);
444 return XmlConvert.ToInt64 (s);
446 return XmlConvert.ToByte (s);
447 case "unsignedShort":
448 return XmlConvert.ToUInt16 (s);
450 return XmlConvert.ToUInt32 (s);
452 return XmlConvert.ToUInt64 (s);
456 return XmlConvert.ToGuid (s);
460 return (char) XmlConvert.ToUInt32 (s);
462 throw new Exception ("Unanticipated primitive type: " + name);
466 List<SerializationMap> contracts = new List<SerializationMap> ();
468 public KnownTypeCollection ()
472 protected override void ClearItems ()
477 protected override void InsertItem (int index, Type type)
479 if (ShouldNotRegister (type))
481 if (!Contains (type)) {
483 base.InsertItem (index, type);
487 // FIXME: it could remove other types' dependencies.
488 protected override void RemoveItem (int index)
491 DoRemoveItem (index);
494 void DoRemoveItem (int index)
496 Type t = base [index];
497 List<SerializationMap> l = new List<SerializationMap> ();
498 foreach (SerializationMap m in contracts) {
499 if (m.RuntimeType == t)
502 foreach (SerializationMap m in l) {
503 contracts.Remove (m);
504 base.RemoveItem (index);
508 protected override void SetItem (int index, Type type)
510 if (ShouldNotRegister (type))
513 // Since this collection is not assured to be ordered, it ignores the whole Set operation if the type already exists.
519 if (TryRegister (type))
520 base.InsertItem (index - 1, type);
523 internal SerializationMap FindUserMap (Type type)
526 for (int i = 0; i < contracts.Count; i++)
527 if (type == contracts [i].RuntimeType)
528 return contracts [i];
533 internal SerializationMap FindUserMap (QName qname)
536 return contracts.FirstOrDefault (c => c.XmlName == qname);
539 internal SerializationMap FindUserMap (QName qname, Type type)
542 return contracts.FirstOrDefault (c => c.XmlName == qname && c.RuntimeType == type);
545 internal Type GetSerializedType (Type type)
547 if (IsPrimitiveNotEnum (type))
549 Type element = GetCollectionElementType (type);
552 QName name = GetQName (type);
553 var map = FindUserMap (name, type);
555 return map.RuntimeType;
559 internal QName GetQName (Type type)
561 SerializationMap map = FindUserMap (type);
565 return GetStaticQName (type);
568 public static QName GetStaticQName (Type type)
570 if (IsPrimitiveNotEnum (type))
571 return GetPrimitiveTypeName (type);
574 return GetEnumQName (type);
576 QName qname = GetContractQName (type);
580 if (type.GetInterface ("System.Xml.Serialization.IXmlSerializable") != null)
581 //FIXME: Reusing GetSerializableQName here, since we just
582 //need name of the type..
583 return GetSerializableQName (type);
585 qname = GetCollectionContractQName (type);
589 Type element = GetCollectionElementType (type);
590 if (element != null) {
591 if (type.IsInterface || IsCustomCollectionType (type, element))
592 return GetCollectionQName (element);
595 if (GetAttribute<SerializableAttribute> (type) != null)
596 return GetSerializableQName (type);
598 // default type map - still uses GetContractQName().
599 return GetContractQName (type, null, null);
602 internal static QName GetContractQName (Type type)
604 var a = GetAttribute<DataContractAttribute> (type);
605 return a == null ? null : GetContractQName (type, a.Name, a.Namespace);
608 static QName GetCollectionContractQName (Type type)
610 var a = GetAttribute<CollectionDataContractAttribute> (type);
611 return a == null ? null : GetContractQName (type, a.Name, a.Namespace);
614 static QName GetContractQName (Type type, string name, string ns)
617 name = GetDefaultName (type);
618 else if (type.IsGenericType) {
619 var args = type.GetGenericArguments ();
620 for (int i = 0; i < args.Length; i++)
621 name = name.Replace ("{" + i + "}", GetStaticQName (args [i]).Name);
625 ns = GetDefaultNamespace (type);
626 return new QName (name, ns);
629 static QName GetEnumQName (Type type)
631 string name = null, ns = null;
636 var dca = GetAttribute<DataContractAttribute> (type);
644 ns = GetDefaultNamespace (type);
647 name = type.Namespace == null ? type.Name : type.FullName.Substring (type.Namespace.Length + 1).Replace ('+', '.');
649 return new QName (name, ns);
652 internal static string GetDefaultName (Type type)
654 // FIXME: there could be decent ways to get
655 // the same result...
656 string name = type.Namespace == null || type.Namespace.Length == 0 ? type.Name : type.FullName.Substring (type.Namespace.Length + 1).Replace ('+', '.');
657 if (type.IsGenericType) {
658 name = name.Substring (0, name.IndexOf ('`')) + "Of";
659 foreach (var t in type.GetGenericArguments ())
660 name += t.Name; // FIXME: check namespaces too
665 internal static string GetDefaultNamespace (Type type)
667 foreach (ContractNamespaceAttribute a in type.Assembly.GetCustomAttributes (typeof (ContractNamespaceAttribute), true))
668 if (a.ClrNamespace == type.Namespace)
669 return a.ContractNamespace;
670 return DefaultClrNamespaceBase + type.Namespace;
673 static QName GetCollectionQName (Type element)
675 QName eqname = GetStaticQName (element);
677 string ns = eqname.Namespace;
678 if (eqname.Namespace == MSSimpleNamespace)
679 //Arrays of Primitive types
680 ns = MSArraysNamespace;
683 "ArrayOf" + XmlConvert.EncodeLocalName (eqname.Name),
687 static QName GetSerializableQName (Type type)
689 // First, check XmlSchemaProviderAttribute and try GetSchema() to see if it returns a schema in the expected format.
690 var xpa = type.GetCustomAttribute<XmlSchemaProviderAttribute> (true);
693 return XmlQualifiedName.Empty;
694 var mi = type.GetMethod (xpa.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
697 var xss = new XmlSchemaSet ();
698 return (XmlQualifiedName) mi.Invoke (null, new object [] {xss});
705 string xmlName = type.Name;
706 if (type.IsGenericType) {
707 xmlName = xmlName.Substring (0, xmlName.IndexOf ('`')) + "Of";
708 foreach (var t in type.GetGenericArguments ())
709 xmlName += GetStaticQName (t).Name; // FIXME: check namespaces too
711 string xmlNamespace = GetDefaultNamespace (type);
712 var x = GetAttribute<XmlRootAttribute> (type);
714 xmlName = x.ElementName;
715 xmlNamespace = x.Namespace;
717 return new QName (XmlConvert.EncodeLocalName (xmlName), xmlNamespace);
720 static bool IsPrimitiveNotEnum (Type type)
724 if (Type.GetTypeCode (type) != TypeCode.Object) // explicitly primitive
726 if (type == typeof (Guid) || type == typeof (object) || type == typeof(TimeSpan) || type == typeof(byte[]) || type == typeof(Uri) || type == typeof(DateTimeOffset)) // special primitives
729 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 (!QName.Empty.Equals (qname) && 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);