Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / System.Runtime.Serialization / System.Runtime.Serialization / DataContractSerializer.cs
index 6693daa0e7260fdb3e84a718a80546996e86f3e4..8e713d78eb17b74b356b275963efe1fce6d35c9c 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
@@ -25,7 +25,7 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-#if NET_2_0
+
 using System;
 using System.Collections;
 using System.Collections.Generic;
@@ -51,7 +51,9 @@ namespace System.Runtime.Serialization
                StreamingContext context;
                ReadOnlyCollection<Type> returned_known_types;
                KnownTypeCollection known_types;
+               List<Type> specified_known_types;
                IDataContractSurrogate surrogate;
+               DataContractResolver resolver, default_resolver;
 
                int max_items = 0x10000; // FIXME: could be from config.
 
@@ -70,13 +72,12 @@ namespace System.Runtime.Serialization
                        if (type == null)
                                throw new ArgumentNullException ("type");
                        this.type = type;
-                       known_types = new KnownTypeCollection ();
                        PopulateTypes (knownTypes);
-                       known_types.TryRegister (type);
+                       known_types.Add (type);
                        QName qname = known_types.GetQName (type);
 
                        FillDictionaryString (qname.Name, qname.Namespace);
-                       
+
                }
 
                public DataContractSerializer (Type type, string rootName,
@@ -213,25 +214,53 @@ namespace System.Runtime.Serialization
                }
 #endif
 
+#if NET_4_5
+               public DataContractSerializer (Type type, DataContractSerializerSettings settings)
+                       : this (type, settings.RootName, settings.RootNamespace, settings.KnownTypes,
+                               settings.MaxItemsInObjectGraph, settings.IgnoreExtensionDataObject,
+                               settings.PreserveObjectReferences, settings.DataContractSurrogate,
+                               settings.DataContractResolver)
+               {
+               }
+#endif
+
                void PopulateTypes (IEnumerable<Type> knownTypes)
                {
                        if (known_types == null)
-                               known_types= new KnownTypeCollection ();
+                               known_types = new KnownTypeCollection ();
+
+                       if (specified_known_types == null)
+                               specified_known_types = new List<Type> ();
 
                        if (knownTypes != null) {
-                               foreach (Type t in knownTypes)
-                                       known_types.TryRegister (t);
+                               foreach (Type t in knownTypes) {
+                                       known_types.Add (t);
+                                       specified_known_types.Add (t);
+                               }
                        }
 
+                       RegisterTypeAsKnown (type);
+               }
+
+               void RegisterTypeAsKnown (Type type)
+               {
+                       if (known_types.Contains (type))
+                               return;
+
                        Type elementType = type;
-                       if (type.HasElementType)
+                       if (type.HasElementType) {
+                               known_types.Add (type);
                                elementType = type.GetElementType ();
+                       }
+
+                       known_types.Add (elementType);
 
                        /* Get all KnownTypeAttribute-s, including inherited ones */
                        object [] attrs = elementType.GetCustomAttributes (typeof (KnownTypeAttribute), true);
                        for (int i = 0; i < attrs.Length; i ++) {
                                KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
-                               known_types.TryRegister (kt.Type);
+                               foreach (var t in kt.GetTypes (elementType))
+                                       RegisterTypeAsKnown (t);
                        }
                }
 
@@ -255,8 +284,6 @@ namespace System.Runtime.Serialization
                        ignore_ext = ignoreExtensionDataObject;
                        preserve_refs = preserveObjectReferences;
                        surrogate = dataContractSurrogate;
-
-                       PopulateTypes (Type.EmptyTypes);
                }
 
 #if NET_4_0
@@ -264,7 +291,13 @@ namespace System.Runtime.Serialization
 #else
                internal
 #endif
-               DataContractResolver DataContractResolver { get; private set; }
+               DataContractResolver DataContractResolver {
+                       get { return resolver; }
+                       private set {
+                               resolver = value;
+                               default_resolver = default_resolver ?? new DefaultDataContractResolver (this);
+                       }
+               }
 
                public bool IgnoreExtensionDataObject {
                        get { return ignore_ext; }
@@ -273,11 +306,15 @@ namespace System.Runtime.Serialization
                public ReadOnlyCollection<Type> KnownTypes {
                        get {
                                if (returned_known_types == null)
-                                       returned_known_types = new ReadOnlyCollection<Type> (known_types);
+                                       returned_known_types = new ReadOnlyCollection<Type> (specified_known_types);
                                return returned_known_types;
                        }
                }
 
+               internal KnownTypeCollection InternalKnownTypes {
+                       get { return known_types; }
+               }
+
                public IDataContractSurrogate DataContractSurrogate {
                        get { return surrogate; }
                }
@@ -323,7 +360,7 @@ namespace System.Runtime.Serialization
                        bool isEmpty = reader.IsEmptyElement;
 
                        object ret = XmlFormatterDeserializer.Deserialize (reader, type,
-                               known_types, surrogate, DataContractResolver, root_name.Value, root_ns.Value, verifyObjectName);
+                               known_types, surrogate, DataContractResolver, default_resolver, root_name.Value, root_ns.Value, verifyObjectName);
 
                        // remove temporarily-added known types for
                        // rootType and object graph type.
@@ -346,18 +383,6 @@ namespace System.Runtime.Serialization
                }
 #endif
 
-               private void ReadRootStartElement (XmlReader reader, Type type)
-               {
-                       SerializationMap map =
-                               known_types.FindUserMap (type);
-                       QName name = map != null ? map.XmlName :
-                               KnownTypeCollection.GetPredefinedTypeName (type);
-                       reader.MoveToContent ();
-                       reader.ReadStartElement (name.Name, name.Namespace);
-                       // FIXME: could there be any attributes to handle here?
-                       reader.Read ();
-               }
-
                // SP1
                public override void WriteObject (XmlWriter writer, object graph)
                {
@@ -378,7 +403,7 @@ namespace System.Runtime.Serialization
                }
 #endif
 
-               [MonoTODO ("support arrays; support Serializable; support SharedType; use DataContractSurrogate")]
+               [MonoTODO ("use DataContractSurrogate")]
                /*
                        when writeContentOnly is true, then the input XmlWriter
                        must be at element state. This is to write possible
@@ -399,8 +424,8 @@ namespace System.Runtime.Serialization
                        int startTypeCount = known_types.Count;
 
                        XmlFormatterSerializer.Serialize (writer, graph,
-                               known_types,
-                               ignore_ext, max_items, root_ns.Value, preserve_refs);
+                               type, known_types,
+                               ignore_ext, max_items, root_ns.Value, preserve_refs, DataContractResolver, default_resolver);
 
                        // remove temporarily-added known types for
                        // rootType and object graph type.
@@ -425,7 +450,7 @@ namespace System.Runtime.Serialization
                        XmlDictionaryWriter writer, object graph)
                {
                        Type rootType = type;
-                       
+
                        if (root_name.Value == "")
                                throw new InvalidDataContractException ("Type '" + type.ToString () +
                                        "' cannot have a DataContract attribute Name set to null or empty string.");
@@ -440,34 +465,48 @@ namespace System.Runtime.Serialization
                                return;
                        }
 
-                       QName instName = null;
-                       QName root_qname = known_types.GetQName (rootType);
-                       QName graph_qname = known_types.GetQName (graph.GetType ());
+                       QName rootQName = null;
+                       XmlDictionaryString name, ns;
+                       var graphType = graph.GetType ();
+                       if (DataContractResolver != null && DataContractResolver.TryResolveType (graphType, type, default_resolver, out name, out ns))
+                               rootQName = new QName (name.Value, ns.Value);
+
+                       // It is error unless 1) TypeResolver resolved the type name, 2) the object is the exact type, 3) the object is known or 4) the type is primitive.
+
+                       QName collectionQName;
+                       if (KnownTypeCollection.IsInterchangeableCollectionType (type, graphType, out collectionQName)) {
+                               graphType = type;
+                               rootQName = collectionQName;
+                       } else if (graphType != type && rootQName == null && IsUnknownType (type, graphType))
+                               throw new SerializationException (String.Format ("Type '{0}' is unexpected. The type should either be registered as a known type, or DataContractResolver should be used.", graphType));
 
-                       known_types.Add (graph.GetType ());
+                       QName instName = rootQName;
+                       rootQName = rootQName ?? known_types.GetQName (rootType);
+                       QName graph_qname = known_types.GetQName (graphType);
+
+                       known_types.Add (graphType);
 
                        if (names_filled)
                                writer.WriteStartElement (root_name.Value, root_ns.Value);
                        else
                                writer.WriteStartElement (root_name, root_ns);
-                       if (root_ns.Value != root_qname.Namespace)
-                               if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace)
-                                       writer.WriteXmlnsAttribute (null, root_qname.Namespace);
 
-                       if (root_qname == graph_qname) {
-                               if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace &&
-                                       !rootType.IsEnum)
-                                       //FIXME: Hack, when should the "i:type" be written?
-                                       //Not used in case of enums
-                                       writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
+                       if (rootQName != graph_qname || rootQName.Namespace != KnownTypeCollection.MSSimpleNamespace && !rootType.IsEnum)
+                               //FIXME: Hack, when should the "i:type" be written?
+                               //Not used in case of enums
+                               writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
+
+                       if (root_ns.Value != rootQName.Namespace)
+                               if (rootQName.Namespace != KnownTypeCollection.MSSimpleNamespace)
+                                       writer.WriteXmlnsAttribute (null, rootQName.Namespace);
 
+                       if (rootQName == graph_qname)
                                return;
-                       }
 
                        /* Different names */
                        known_types.Add (rootType);
-                       
-                       instName = KnownTypeCollection.GetPredefinedTypeName (graph.GetType ());
+
+                       instName = instName ?? KnownTypeCollection.GetPredefinedTypeName (graphType);
                        if (instName == QName.Empty)
                                /* Not a primitive type */
                                instName = graph_qname;
@@ -475,10 +514,32 @@ namespace System.Runtime.Serialization
                                /* FIXME: Hack, .. see test WriteObject7 () */
                                instName = new QName (instName.Name, XmlSchema.Namespace);
 
+/* // disabled as it now generates extraneous i:type output.
                        // output xsi:type as rootType is not equivalent to the graph's type.
                        writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
                        writer.WriteQualifiedName (instName.Name, instName.Namespace);
                        writer.WriteEndAttribute ();
+*/
+               }
+
+               bool IsUnknownType (Type contractType, Type type)
+               {
+                       if (type.IsArray) {
+                               if (KnownTypeCollection.GetAttribute<CollectionDataContractAttribute> (contractType) != null ||
+                                   KnownTypeCollection.GetAttribute<DataContractAttribute> (contractType) != null)
+                                       return true;
+                       }
+                       return IsUnknownType (type);
+               }
+
+               bool IsUnknownType (Type type)
+               {
+                       if (known_types.Contains (type) ||
+                           KnownTypeCollection.GetPrimitiveTypeName (type) != QName.Empty)
+                               return false;
+                       if (type.IsArray)
+                               return IsUnknownType (type.GetElementType ());
+                       return true;
                }
 
                public override void WriteEndObject (XmlDictionaryWriter writer)
@@ -491,6 +552,12 @@ namespace System.Runtime.Serialization
                {
                        WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));
                }
+
+#if NET_4_5
+               [MonoTODO]
+               public bool SerializeReadOnlyTypes {
+                       get { throw new NotImplementedException (); }
+               }
+#endif
        }
 }
-#endif