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);
692 var mi = type.GetMethod (xpa.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
695 var xss = new XmlSchemaSet ();
696 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
727 if (type == typeof (XmlElement) || type == typeof (XmlNode []))
730 if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
731 return IsPrimitiveNotEnum (type.GetGenericArguments () [0]);
735 bool ShouldNotRegister (Type type)
737 return IsPrimitiveNotEnum (type);
740 internal bool TryRegister (Type type)
743 return DoTryRegister (type);
747 bool DoTryRegister (Type type)
749 // exclude predefined maps
750 if (ShouldNotRegister (type))
753 if (FindUserMap (type) != null)
756 if (RegisterEnum (type) != null)
759 if (RegisterDictionary (type) != null)
762 if (RegisterCollectionContract (type) != null)
765 if (RegisterContract (type) != null)
768 if (RegisterIXmlSerializable (type) != null)
771 if (RegisterCollection (type) != null)
774 if (GetAttribute<SerializableAttribute> (type) != null) {
775 RegisterSerializable (type);
779 RegisterDefaultTypeMap (type);
783 static Type GetCollectionElementType (Type type)
786 return type.GetElementType ();
787 var ifaces = type.GetInterfacesOrSelfInterface ();
788 foreach (Type i in ifaces)
789 if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (IEnumerable<>)))
790 return i.GetGenericArguments () [0];
791 foreach (Type i in ifaces)
792 if (i == typeof (IEnumerable))
793 return typeof (object);
797 internal static T GetAttribute<T> (ICustomAttributeProvider ap) where T : Attribute
799 object [] atts = ap.GetCustomAttributes (typeof (T), false);
800 return atts.Length == 0 ? null : (T) atts [0];
803 private CollectionContractTypeMap RegisterCollectionContract (Type type)
805 var cdca = GetAttribute<CollectionDataContractAttribute> (type);
809 Type element = GetCollectionElementType (type);
811 throw new InvalidDataContractException (String.Format ("Type '{0}' is marked as collection contract, but it is not a collection", type));
812 if (type.GetMethod ("Add", new Type[] { element }) == null)
813 throw new InvalidDataContractException (String.Format ("Type '{0}' is marked as collection contract, but missing a public \"Add\" method", type));
815 TryRegister (element); // must be registered before the name conflict check.
817 QName qname = GetCollectionContractQName (type);
818 CheckStandardQName (qname);
819 var map = FindUserMap (qname, type);
821 var cmap = map as CollectionContractTypeMap;
822 if (cmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
823 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));
826 var ret = new CollectionContractTypeMap (type, cdca, element, qname, this);
831 private CollectionTypeMap RegisterCollection (Type type)
833 Type element = GetCollectionElementType (type);
837 TryRegister (element);
840 * To qualify as a custom collection type, a type must have
841 * a public parameterless constructor and an "Add" method
842 * with the correct parameter type in addition to implementing
843 * one of the collection interfaces.
847 if (!type.IsArray && type.IsClass && !IsCustomCollectionType (type, element))
850 QName qname = GetCollectionQName (element);
852 var map = FindUserMap (qname, element);
854 var cmap = map as CollectionTypeMap;
855 if (cmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
856 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));
860 CollectionTypeMap ret =
861 new CollectionTypeMap (type, element, qname, this);
866 static bool IsCustomCollectionType (Type type, Type elementType)
870 if (type.GetConstructor (new Type [0]) == null)
872 if (type.GetMethod ("Add", new Type[] { elementType }) == null)
878 internal static bool IsInterchangeableCollectionType (Type contractType, Type graphType,
879 out QName collectionQName)
881 collectionQName = null;
882 if (GetAttribute<CollectionDataContractAttribute> (contractType) != null)
885 var type = contractType;
886 if (type.IsGenericType)
887 type = type.GetGenericTypeDefinition ();
889 var elementType = GetCollectionElementType (contractType);
890 if (elementType == null)
893 if (contractType.IsArray) {
894 if (!graphType.IsArray || !elementType.Equals (graphType.GetElementType ()))
895 throw new InvalidCastException (String.Format ("Type '{0}' cannot be converted into '{1}'.", graphType.GetElementType (), elementType));
896 } else if (!contractType.IsInterface) {
897 if (GetAttribute<SerializableAttribute> (contractType) == null)
900 var graphElementType = GetCollectionElementType (graphType);
901 if (elementType != graphElementType)
904 if (!IsCustomCollectionType (contractType, elementType))
906 } else if (type.Equals (typeof (IEnumerable)) || type.Equals (typeof (IList)) ||
907 type.Equals (typeof (ICollection))) {
908 if (!graphType.ImplementsInterface (contractType))
910 } else if (type.Equals (typeof (IEnumerable<>)) || type.Equals (typeof (IList<>)) ||
911 type.Equals (typeof (ICollection<>))) {
912 var graphElementType = GetCollectionElementType (graphType);
913 if (graphElementType != elementType)
914 throw new InvalidCastException (String.Format (
915 "Cannot convert type '{0}' into '{1}'.", graphType, contractType));
917 if (!graphType.ImplementsInterface (contractType))
923 collectionQName = GetCollectionQName (elementType);
927 static bool ImplementsInterface (Type type, Type iface)
929 foreach (var i in type.GetInterfacesOrSelfInterface ())
937 static bool TypeImplementsIEnumerable (Type type)
939 foreach (var iface in type.GetInterfacesOrSelfInterface ())
940 if (iface == typeof (IEnumerable) || (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IEnumerable<>)))
946 static bool TypeImplementsIDictionary (Type type)
948 foreach (var iface in type.GetInterfacesOrSelfInterface ())
949 if (iface == typeof (IDictionary) || (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>)))
955 // it also supports contract-based dictionary.
956 private DictionaryTypeMap RegisterDictionary (Type type)
958 if (!TypeImplementsIDictionary (type))
961 var cdca = GetAttribute<CollectionDataContractAttribute> (type);
963 DictionaryTypeMap ret =
964 new DictionaryTypeMap (type, cdca, this);
966 TryRegister (ret.KeyType);
967 TryRegister (ret.ValueType);
969 var map = FindUserMap (ret.XmlName, type);
971 var dmap = map as DictionaryTypeMap;
972 if (dmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
973 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));
980 private SerializationMap RegisterSerializable (Type type)
982 QName qname = GetSerializableQName (type);
984 if (FindUserMap (qname, type) != null)
985 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
987 SharedTypeMap ret = new SharedTypeMap (type, qname, this);
993 private SerializationMap RegisterIXmlSerializable (Type type)
995 if (type.GetInterface ("System.Xml.Serialization.IXmlSerializable") == null)
998 QName qname = GetSerializableQName (type);
1000 if (FindUserMap (qname, type) != null)
1001 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
1003 XmlSerializableMap ret = new XmlSerializableMap (type, qname, this);
1004 contracts.Add (ret);
1009 void CheckStandardQName (QName qname)
1011 switch (qname.Namespace) {
1012 case XmlSchema.Namespace:
1013 case XmlSchema.InstanceNamespace:
1014 case MSSimpleNamespace:
1015 case MSArraysNamespace:
1016 throw new InvalidOperationException (String.Format ("Namespace {0} is reserved and cannot be used for user serialization", qname.Namespace));
1021 private SharedContractMap RegisterContract (Type type)
1023 QName qname = GetContractQName (type);
1026 CheckStandardQName (qname);
1027 if (FindUserMap (qname, type) != null)
1028 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
1030 SharedContractMap ret = new SharedContractMap (type, qname, this);
1031 contracts.Add (ret);
1034 if (type.BaseType != typeof (object)) {
1035 TryRegister (type.BaseType);
1036 if (!FindUserMap (type.BaseType).IsContractAllowedType)
1037 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));
1040 object [] attrs = type.GetCustomAttributes (typeof (KnownTypeAttribute), true);
1041 for (int i = 0; i < attrs.Length; i++) {
1042 KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
1043 foreach (var t in kt.GetTypes (type))
1050 DefaultTypeMap RegisterDefaultTypeMap (Type type)
1052 DefaultTypeMap ret = new DefaultTypeMap (type, this);
1053 contracts.Add (ret);
1058 private EnumMap RegisterEnum (Type type)
1060 QName qname = GetEnumQName (type);
1064 if (FindUserMap (qname, type) != null)
1065 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
1068 new EnumMap (type, qname, this);
1069 contracts.Add (ret);