Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mcs / class / System.Runtime.Serialization / System.Runtime.Serialization / KnownTypeCollection.cs
index 0c2d7d3e9c6d0cc1844ee03b33fa101d07a8a4fe..43b846aaad72480e334680b124f71208eb7a3f83 100755 (executable)
 // distribute, sublicense, and/or sell copies of the Software, and to
 // permit persons to whom the Software is furnished to do so, subject to
 // the following conditions:
-// 
+//
 // The above copyright notice and this permission notice shall be
 // included in all copies or substantial portions of the Software.
-// 
+//
 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -74,13 +74,37 @@ namespace System.Runtime.Serialization
          exists (and raises InvalidOperationException if required).
 
 */
+
        internal static class TypeExtensions
        {
+#if !NET_4_5
                public static T GetCustomAttribute<T> (this MemberInfo type, bool inherit)
                {
                        var arr = type.GetCustomAttributes (typeof (T), inherit);
                        return arr != null && arr.Length == 1 ? (T) arr [0] : default (T);
                }
+#endif
+               public static IEnumerable<Type> GetInterfacesOrSelfInterface (this Type type)
+               {
+                       if (type.IsInterface)
+                               yield return type;
+                       foreach (var t in type.GetInterfaces ())
+                               yield return t;
+               }
+
+               public static bool ImplementsInterface (this Type type, Type iface)
+               {
+                       foreach (var t in type.GetInterfacesOrSelfInterface ()) {
+                               if (t == iface)
+                                       return true;
+                       }
+
+                       var baseType = type.BaseType;
+                       if (baseType != null)
+                               return baseType.ImplementsInterface (iface);
+                       
+                       return false;
+               }
        }
 
        internal sealed class KnownTypeCollection : Collection<Type>
@@ -91,6 +115,9 @@ namespace System.Runtime.Serialization
                        "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
                internal const string DefaultClrNamespaceBase =
                        "http://schemas.datacontract.org/2004/07/";
+               internal const string DefaultClrNamespaceSystem =
+                       "http://schemas.datacontract.org/2004/07/System";
+
 
                static QName any_type, bool_type,
                        byte_type, date_type, decimal_type, double_type,
@@ -102,14 +129,13 @@ namespace System.Runtime.Serialization
                        // custom in ms nsURI schema
                        char_type, guid_type,
                        // not in ms nsURI schema
-                       dbnull_type;
+                       dbnull_type, date_time_offset_type;
+
+               // 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 :-(
+               static Dictionary<string,Type> xs_predefined_types = new Dictionary<string,Type> ();
 
                static KnownTypeCollection ()
                {
-                       //any_type, bool_type,  byte_type, date_type, decimal_type, double_type,        float_type, string_type,
-                       // short_type, int_type, long_type,     ubyte_type, ushort_type, uint_type, ulong_type,
-                       //      any_uri_type, base64_type, duration_type, qname_type,
-                       //      char_type, guid_type,   dbnull_type;
                        string s = MSSimpleNamespace;
                        any_type = new QName ("anyType", s);
                        any_uri_type = new QName ("anyURI", s);
@@ -133,7 +159,57 @@ namespace System.Runtime.Serialization
                        guid_type = new QName ("guid", s);
                        char_type = new QName ("char", s);
 
-                       dbnull_type = new QName ("DBNull", MSSimpleNamespace + "System");
+                       dbnull_type = new QName ("DBNull", DefaultClrNamespaceBase + "System");
+                       date_time_offset_type = new QName ("DateTimeOffset", DefaultClrNamespaceBase + "System");
+
+                       xs_predefined_types.Add ("string", typeof (string));
+                       xs_predefined_types.Add ("boolean", typeof (bool));
+                       xs_predefined_types.Add ("float", typeof (float));
+                       xs_predefined_types.Add ("double", typeof (double));
+                       xs_predefined_types.Add ("decimal", typeof (decimal));
+                       xs_predefined_types.Add ("duration", typeof (TimeSpan));
+                       xs_predefined_types.Add ("dateTime", typeof (DateTime));
+                       xs_predefined_types.Add ("date", typeof (DateTime));
+                       xs_predefined_types.Add ("time", typeof (DateTime));
+                       xs_predefined_types.Add ("gYearMonth", typeof (DateTime));
+                       xs_predefined_types.Add ("gYear", typeof (DateTime));
+                       xs_predefined_types.Add ("gMonthDay", typeof (DateTime));
+                       xs_predefined_types.Add ("gDay", typeof (DateTime));
+                       xs_predefined_types.Add ("gMonth", typeof (DateTime));
+                       xs_predefined_types.Add ("hexBinary", typeof (byte []));
+                       xs_predefined_types.Add ("base64Binary", typeof (byte []));
+                       xs_predefined_types.Add ("anyURI", typeof (Uri));
+                       xs_predefined_types.Add ("QName", typeof (QName));
+                       xs_predefined_types.Add ("NOTATION", typeof (string));
+
+                       xs_predefined_types.Add ("normalizedString", typeof (string));
+                       xs_predefined_types.Add ("token", typeof (string));
+                       xs_predefined_types.Add ("language", typeof (string));
+                       xs_predefined_types.Add ("IDREFS", typeof (string []));
+                       xs_predefined_types.Add ("ENTITIES", typeof (string []));
+                       xs_predefined_types.Add ("NMTOKEN", typeof (string));
+                       xs_predefined_types.Add ("NMTOKENS", typeof (string []));
+                       xs_predefined_types.Add ("Name", typeof (string));
+                       xs_predefined_types.Add ("NCName", typeof (string));
+                       xs_predefined_types.Add ("ID", typeof (string));
+                       xs_predefined_types.Add ("IDREF", typeof (string));
+                       xs_predefined_types.Add ("ENTITY", typeof (string));
+
+                       xs_predefined_types.Add ("integer", typeof (decimal));
+                       xs_predefined_types.Add ("nonPositiveInteger", typeof (int));
+                       xs_predefined_types.Add ("negativeInteger", typeof (int));
+                       xs_predefined_types.Add ("long", typeof (long));
+                       xs_predefined_types.Add ("int", typeof (int));
+                       xs_predefined_types.Add ("short", typeof (short));
+                       xs_predefined_types.Add ("byte", typeof (sbyte));
+                       xs_predefined_types.Add ("nonNegativeInteger", typeof (decimal));
+                       xs_predefined_types.Add ("unsignedLong", typeof (ulong));
+                       xs_predefined_types.Add ("unsignedInt", typeof (uint));
+                       xs_predefined_types.Add ("unsignedShort", typeof (ushort));
+                       xs_predefined_types.Add ("unsignedByte", typeof (byte));
+                       xs_predefined_types.Add ("positiveInteger", typeof (decimal));
+
+                       xs_predefined_types.Add ("anyType", typeof (object));
                }
 
                // FIXME: find out how QName and guid are processed
@@ -179,6 +255,8 @@ namespace System.Runtime.Serialization
                                        return base64_type;
                                if (type == typeof (Uri))
                                        return any_uri_type;
+                               if (type == typeof (DateTimeOffset))
+                                       return date_time_offset_type;
                                return QName.Empty;
                        case TypeCode.Boolean:
                                return bool_type;
@@ -266,55 +344,67 @@ namespace System.Runtime.Serialization
                        }
                }
 
-               // FIXME: xsd types and ms serialization types should be differentiated.
-               internal static Type GetPrimitiveTypeFromName (string name)
+               internal static Type GetPrimitiveTypeFromName (QName name)
                {
-                       switch (name) {
-                       case "anyURI":
-                               return typeof (Uri);
-                       case "boolean":
-                               return typeof (bool);
-                       case "base64Binary":
-                               return typeof (byte []);
-                       case "dateTime":
-                               return typeof (DateTime);
-                       case "duration":
-                               return typeof (TimeSpan);
-                       case "QName":
-                               return typeof (QName);
-                       case "decimal":
-                               return typeof (decimal);
-                       case "double":
-                               return typeof (double);
-                       case "float":
-                               return typeof (float);
-                       case "byte":
-                               return typeof (sbyte);
-                       case "short":
-                               return typeof (short);
-                       case "int":
-                               return typeof (int);
-                       case "long":
-                               return typeof (long);
-                       case "unsignedByte":
-                               return typeof (byte);
-                       case "unsignedShort":
-                               return typeof (ushort);
-                       case "unsignedInt":
-                               return typeof (uint);
-                       case "unsignedLong":
-                               return typeof (ulong);
-                       case "string":
-                               return typeof (string);
-                       case "anyType":
-                               return typeof (object);
-                       case "guid":
-                               return typeof (Guid);
-                       case "char":
-                               return typeof (char);
-                       default:
-                               return null;
+                       switch (name.Namespace) {
+                       case DefaultClrNamespaceSystem:
+                               switch (name.Name) {
+                               case "DBNull":
+                                       return typeof (DBNull);
+                               case "DateTimeOffset":
+                                       return typeof (DateTimeOffset);
+                               }
+                               break;
+                       case XmlSchema.Namespace:
+                               return xs_predefined_types.FirstOrDefault (p => p.Key == name.Name).Value;
+                       case MSSimpleNamespace:
+                               switch (name.Name) {
+                               case "anyURI":
+                                       return typeof (Uri);
+                               case "boolean":
+                                       return typeof (bool);
+                               case "base64Binary":
+                                       return typeof (byte []);
+                               case "dateTime":
+                                       return typeof (DateTime);
+                               case "duration":
+                                       return typeof (TimeSpan);
+                               case "QName":
+                                       return typeof (QName);
+                               case "decimal":
+                                       return typeof (decimal);
+                               case "double":
+                                       return typeof (double);
+                               case "float":
+                                       return typeof (float);
+                               case "byte":
+                                       return typeof (sbyte);
+                               case "short":
+                                       return typeof (short);
+                               case "int":
+                                       return typeof (int);
+                               case "long":
+                                       return typeof (long);
+                               case "unsignedByte":
+                                       return typeof (byte);
+                               case "unsignedShort":
+                                       return typeof (ushort);
+                               case "unsignedInt":
+                                       return typeof (uint);
+                               case "unsignedLong":
+                                       return typeof (ulong);
+                               case "string":
+                                       return typeof (string);
+                               case "anyType":
+                                       return typeof (object);
+                               case "guid":
+                                       return typeof (Guid);
+                               case "char":
+                                       return typeof (char);
+                               }
+                               break;
                        }
+                       return null;
                }
 
 
@@ -396,6 +486,12 @@ namespace System.Runtime.Serialization
 
                // FIXME: it could remove other types' dependencies.
                protected override void RemoveItem (int index)
+               {
+                       lock (this)
+                               DoRemoveItem (index);
+               }
+
+               void DoRemoveItem (int index)
                {
                        Type t = base [index];
                        List<SerializationMap> l = new List<SerializationMap> ();
@@ -424,37 +520,48 @@ namespace System.Runtime.Serialization
                                base.InsertItem (index - 1, type);
                }
 
+               internal SerializationMap FindUserMap (Type type)
+               {
+                       lock (this) {
+                               for (int i = 0; i < contracts.Count; i++)
+                                       if (type == contracts [i].RuntimeType)
+                                               return contracts [i];
+                               return null;
+                       }
+               }
+
                internal SerializationMap FindUserMap (QName qname)
                {
-                       return contracts.FirstOrDefault (c => c.XmlName == qname);
+                       lock (this)
+                               return contracts.FirstOrDefault (c => c.XmlName == qname);
+               }
+
+               internal SerializationMap FindUserMap (QName qname, Type type)
+               {
+                       lock (this)
+                               return contracts.FirstOrDefault (c => c.XmlName == qname && c.RuntimeType == type);
                }
 
                internal Type GetSerializedType (Type type)
                {
+                       if (IsPrimitiveNotEnum (type))
+                               return type;
                        Type element = GetCollectionElementType (type);
                        if (element == null)
                                return type;
                        QName name = GetQName (type);
-                       var map = FindUserMap (name);
+                       var map = FindUserMap (name, type);
                        if (map != null)
                                return map.RuntimeType;
                        return type;
                }
 
-               internal SerializationMap FindUserMap (Type type)
-               {
-                       for (int i = 0; i < contracts.Count; i++)
-                               if (type == contracts [i].RuntimeType)
-                                       return contracts [i];
-                       return null;
-               }
-
                internal QName GetQName (Type type)
                {
                        SerializationMap map = FindUserMap (type);
                        if (map != null)
                                // already mapped.
-                               return map.XmlName; 
+                               return map.XmlName;
                        return GetStaticQName (type);
                }
 
@@ -471,7 +578,7 @@ namespace System.Runtime.Serialization
                                return qname;
 
                        if (type.GetInterface ("System.Xml.Serialization.IXmlSerializable") != null)
-                               //FIXME: Reusing GetSerializableQName here, since we just 
+                               //FIXME: Reusing GetSerializableQName here, since we just
                                //need name of the type..
                                return GetSerializableQName (type);
 
@@ -480,8 +587,10 @@ namespace System.Runtime.Serialization
                                return qname;
 
                        Type element = GetCollectionElementType (type);
-                       if (element != null)
-                               return GetCollectionQName (element);
+                       if (element != null) {
+                               if (type.IsInterface || IsCustomCollectionType (type, element))
+                                       return GetCollectionQName (element);
+                       }
 
                        if (GetAttribute<SerializableAttribute> (type) != null)
                                return GetSerializableQName (type);
@@ -506,6 +615,12 @@ namespace System.Runtime.Serialization
                {
                        if (name == null)
                                name = GetDefaultName (type);
+                       else if (type.IsGenericType) {
+                               var args = type.GetGenericArguments ();
+                               for (int i = 0; i < args.Length; i++)
+                                       name = name.Replace ("{" + i + "}", GetStaticQName (args [i]).Name);
+                       }
+
                        if (ns == null)
                                ns = GetDefaultNamespace (type);
                        return new QName (name, ns);
@@ -536,7 +651,7 @@ namespace System.Runtime.Serialization
 
                internal static string GetDefaultName (Type type)
                {
-                       // FIXME: there could be decent ways to get 
+                       // FIXME: there could be decent ways to get
                        // the same result...
                        string name = type.Namespace == null || type.Namespace.Length == 0 ? type.Name : type.FullName.Substring (type.Namespace.Length + 1).Replace ('+', '.');
                        if (type.IsGenericType) {
@@ -558,7 +673,7 @@ namespace System.Runtime.Serialization
                static QName GetCollectionQName (Type element)
                {
                        QName eqname = GetStaticQName (element);
-                       
+
                        string ns = eqname.Namespace;
                        if (eqname.Namespace == MSSimpleNamespace)
                                //Arrays of Primitive types
@@ -571,7 +686,7 @@ namespace System.Runtime.Serialization
 
                static QName GetSerializableQName (Type type)
                {
-#if !NET_2_1
+#if !MOONLIGHT
                        // First, check XmlSchemaProviderAttribute and try GetSchema() to see if it returns a schema in the expected format.
                        var xpa = type.GetCustomAttribute<XmlSchemaProviderAttribute> (true);
                        if (xpa != null) {
@@ -608,8 +723,13 @@ namespace System.Runtime.Serialization
                                return false;
                        if (Type.GetTypeCode (type) != TypeCode.Object) // explicitly primitive
                                return true;
-                       if (type == typeof (Guid) || type == typeof (object) || type == typeof(TimeSpan) || type == typeof(byte[]) || type==typeof(Uri)) // special primitives
+                       if (type == typeof (Guid) || type == typeof (object) || type == typeof(TimeSpan) || type == typeof(byte[]) || type == typeof(Uri) || type == typeof(DateTimeOffset)) // special primitives
                                return true;
+#if !MOONLIGHT
+                       // DOM nodes
+                       if (type == typeof (XmlElement) || type == typeof (XmlNode []))
+                               return true;
+#endif
                        // nullable
                        if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
                                return IsPrimitiveNotEnum (type.GetGenericArguments () [0]);
@@ -622,6 +742,13 @@ namespace System.Runtime.Serialization
                }
 
                internal bool TryRegister (Type type)
+               {
+                       lock (this) {
+                               return DoTryRegister (type);
+                       }
+               }
+
+               bool DoTryRegister (Type type)
                {
                        // exclude predefined maps
                        if (ShouldNotRegister (type))
@@ -661,13 +788,12 @@ namespace System.Runtime.Serialization
                {
                        if (type.IsArray)
                                return type.GetElementType ();
-
-                       Type [] ifaces = type.GetInterfaces ();
+                       var ifaces = type.GetInterfacesOrSelfInterface ();
                        foreach (Type i in ifaces)
-                               if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (ICollection<>)))
+                               if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (IEnumerable<>)))
                                        return i.GetGenericArguments () [0];
                        foreach (Type i in ifaces)
-                               if (i == typeof (IList))
+                               if (i == typeof (IEnumerable))
                                        return typeof (object);
                        return null;
                }
@@ -686,14 +812,20 @@ namespace System.Runtime.Serialization
 
                        Type element = GetCollectionElementType (type);
                        if (element == null)
-                               throw new InvalidOperationException (String.Format ("Type '{0}' is marked as collection contract, but it is not a collection", type));
+                               throw new InvalidDataContractException (String.Format ("Type '{0}' is marked as collection contract, but it is not a collection", type));
+                       if (type.GetMethod ("Add", new Type[] { element }) == null)
+                               throw new InvalidDataContractException (String.Format ("Type '{0}' is marked as collection contract, but missing a public \"Add\" method", type));
 
                        TryRegister (element); // must be registered before the name conflict check.
 
                        QName qname = GetCollectionContractQName (type);
                        CheckStandardQName (qname);
-                       if (FindUserMap (qname) != null)
-                               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));
+                       var map = FindUserMap (qname, type);
+                       if (map != null) {
+                               var cmap = map as CollectionContractTypeMap;
+                               if (cmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
+                                       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));
+                       }
 
                        var ret = new CollectionContractTypeMap (type, cdca, element, qname, this);
                        contracts.Add (ret);
@@ -708,12 +840,23 @@ namespace System.Runtime.Serialization
 
                        TryRegister (element);
 
+                       /*
+                        * To qualify as a custom collection type, a type must have
+                        * a public parameterless constructor and an "Add" method
+                        * with the correct parameter type in addition to implementing
+                        * one of the collection interfaces.
+                        * 
+                        */
+
+                       if (!type.IsArray && type.IsClass && !IsCustomCollectionType (type, element))
+                               return null;
+
                        QName qname = GetCollectionQName (element);
 
-                       var map = FindUserMap (qname);
+                       var map = FindUserMap (qname, element);
                        if (map != null) {
                                var cmap = map as CollectionTypeMap;
-                               if (cmap == null || cmap.RuntimeType != type)
+                               if (cmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
                                        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));
                                return cmap;
                        }
@@ -724,9 +867,89 @@ namespace System.Runtime.Serialization
                        return ret;
                }
 
+               static bool IsCustomCollectionType (Type type, Type elementType)
+               {
+                       if (!type.IsClass)
+                               return false;
+                       if (type.GetConstructor (new Type [0]) == null)
+                               return false;
+                       if (type.GetMethod ("Add", new Type[] { elementType }) == null)
+                               return false;
+
+                       return true;
+               }
+
+               internal static bool IsInterchangeableCollectionType (Type contractType, Type graphType,
+                                                                     out QName collectionQName)
+               {
+                       collectionQName = null;
+                       if (GetAttribute<CollectionDataContractAttribute> (contractType) != null)
+                               return false;
+
+                       var type = contractType;
+                       if (type.IsGenericType)
+                               type = type.GetGenericTypeDefinition ();
+
+                       var elementType = GetCollectionElementType (contractType);
+                       if (elementType == null)
+                               return false;
+                       
+                       if (contractType.IsArray) {
+                               if (!graphType.IsArray || !elementType.Equals (graphType.GetElementType ()))
+                                       throw new InvalidCastException (String.Format ("Type '{0}' cannot be converted into '{1}'.", graphType.GetElementType (), elementType));
+                       } else if (!contractType.IsInterface) {
+                               if (GetAttribute<SerializableAttribute> (contractType) == null)
+                                       return false;
+
+                               var graphElementType = GetCollectionElementType (graphType);
+                               if (elementType != graphElementType)
+                                       return false;
+
+                               if (!IsCustomCollectionType (contractType, elementType))
+                                       return false;
+                       } else if (type.Equals (typeof (IEnumerable)) || type.Equals (typeof (IList)) ||
+                                  type.Equals (typeof (ICollection))) {
+                               if (!graphType.ImplementsInterface (contractType))
+                                       return false;
+                       } else if (type.Equals (typeof (IEnumerable<>)) || type.Equals (typeof (IList<>)) ||
+                                  type.Equals (typeof (ICollection<>))) {
+                               var graphElementType = GetCollectionElementType (graphType);
+                               if (graphElementType != elementType)
+                                       throw new InvalidCastException (String.Format (
+                                               "Cannot convert type '{0}' into '{1}'.", graphType, contractType));
+
+                               if (!graphType.ImplementsInterface (contractType))
+                                       return false;
+                       } else {
+                               return false;
+                       }
+
+                       collectionQName = GetCollectionQName (elementType);
+                       return true;
+               }
+
+               static bool ImplementsInterface (Type type, Type iface)
+               {
+                       foreach (var i in type.GetInterfacesOrSelfInterface ())
+                               if (iface == i)
+                                       return true;
+                                       
+                       return false;
+               }
+
+
+               static bool TypeImplementsIEnumerable (Type type)
+               {
+                       foreach (var iface in type.GetInterfacesOrSelfInterface ())
+                               if (iface == typeof (IEnumerable) || (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IEnumerable<>)))
+                                       return true;
+                       
+                       return false;
+               }
+
                static bool TypeImplementsIDictionary (Type type)
                {
-                       foreach (var iface in type.GetInterfaces ())
+                       foreach (var iface in type.GetInterfacesOrSelfInterface ())
                                if (iface == typeof (IDictionary) || (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>)))
                                        return true;
 
@@ -744,13 +967,17 @@ namespace System.Runtime.Serialization
                        DictionaryTypeMap ret =
                                new DictionaryTypeMap (type, cdca, this);
 
-                       if (FindUserMap (ret.XmlName) != null)
-                               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));
-                       contracts.Add (ret);
-
                        TryRegister (ret.KeyType);
                        TryRegister (ret.ValueType);
 
+                       var map = FindUserMap (ret.XmlName, type);
+                       if (map != null) {
+                               var dmap = map as DictionaryTypeMap;
+                               if (dmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
+                                       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));
+                       }
+                       contracts.Add (ret);
+
                        return ret;
                }
 
@@ -758,7 +985,7 @@ namespace System.Runtime.Serialization
                {
                        QName qname = GetSerializableQName (type);
 
-                       if (FindUserMap (qname) != null)
+                       if (FindUserMap (qname, type) != null)
                                throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
 
                        SharedTypeMap ret = new SharedTypeMap (type, qname, this);
@@ -774,7 +1001,7 @@ namespace System.Runtime.Serialization
 
                        QName qname = GetSerializableQName (type);
 
-                       if (FindUserMap (qname) != null)
+                       if (FindUserMap (qname, type) != null)
                                throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
 
                        XmlSerializableMap ret = new XmlSerializableMap (type, qname, this);
@@ -801,7 +1028,7 @@ namespace System.Runtime.Serialization
                        if (qname == null)
                                return null;
                        CheckStandardQName (qname);
-                       if (FindUserMap (qname) != null)
+                       if (FindUserMap (qname, type) != null)
                                throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
 
                        SharedContractMap ret = new SharedContractMap (type, qname, this);
@@ -817,7 +1044,8 @@ namespace System.Runtime.Serialization
                        object [] attrs = type.GetCustomAttributes (typeof (KnownTypeAttribute), true);
                        for (int i = 0; i < attrs.Length; i++) {
                                KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
-                               TryRegister (kt.Type);
+                               foreach (var t in kt.GetTypes (type))
+                                       TryRegister (t);
                        }
 
                        return ret;
@@ -837,7 +1065,7 @@ namespace System.Runtime.Serialization
                        if (qname == null)
                                return null;
 
-                       if (FindUserMap (qname) != null)
+                       if (FindUserMap (qname, type) != null)
                                throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
 
                        EnumMap ret =