Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / System.Runtime.Serialization / System.Runtime.Serialization / DataContractSerializer.cs
1 //
2 // DataContractSerializer.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005-2007 Novell, Inc.  http://www.novell.com
8 //
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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
27 //
28
29 using System;
30 using System.Collections;
31 using System.Collections.Generic;
32 using System.Collections.ObjectModel;
33 using System.IO;
34 using System.Reflection;
35 using System.Runtime.Serialization.Formatters.Binary;
36 using System.Xml;
37 using System.Xml.Schema;
38
39 using QName = System.Xml.XmlQualifiedName;
40
41 namespace System.Runtime.Serialization
42 {
43         public sealed class DataContractSerializer : XmlObjectSerializer
44         {
45                 const string xmlns = "http://www.w3.org/2000/xmlns/";
46
47                 Type type;
48                 bool ignore_ext, preserve_refs;
49
50                 // This is only for compatible mode.
51                 StreamingContext context;
52                 ReadOnlyCollection<Type> returned_known_types;
53                 KnownTypeCollection known_types;
54                 List<Type> specified_known_types;
55                 IDataContractSurrogate surrogate;
56                 DataContractResolver resolver, default_resolver;
57
58                 int max_items = 0x10000; // FIXME: could be from config.
59
60                 bool names_filled;
61                 XmlDictionaryString root_name, root_ns;
62
63                 public DataContractSerializer (Type type)
64                         : this (type, Type.EmptyTypes)
65                 {
66                         // nothing to do here.
67                 }
68
69                 public DataContractSerializer (Type type,
70                         IEnumerable<Type> knownTypes)
71                 {
72                         if (type == null)
73                                 throw new ArgumentNullException ("type");
74                         this.type = type;
75                         PopulateTypes (knownTypes);
76                         known_types.Add (type);
77                         QName qname = known_types.GetQName (type);
78
79                         FillDictionaryString (qname.Name, qname.Namespace);
80
81                 }
82
83                 public DataContractSerializer (Type type, string rootName,
84                         string rootNamespace)
85                         : this (type, rootName, rootNamespace, Type.EmptyTypes)
86                 {
87                         // nothing to do here.
88                 }
89
90                 public DataContractSerializer (Type type,
91                         XmlDictionaryString rootName,
92                         XmlDictionaryString rootNamespace)
93                         : this (type, rootName, rootNamespace, Type.EmptyTypes)
94                 {
95                         // nothing to do here.
96                 }
97
98                 public DataContractSerializer (Type type, string rootName,
99                         string rootNamespace, IEnumerable<Type> knownTypes)
100                 {
101                         if (type == null)
102                                 throw new ArgumentNullException ("type");
103                         if (rootName == null)
104                                 throw new ArgumentNullException ("rootName");
105                         if (rootNamespace == null)
106                                 throw new ArgumentNullException ("rootNamespace");
107                         this.type = type;
108                         PopulateTypes (knownTypes);
109                         FillDictionaryString (rootName, rootNamespace);
110                 }
111
112                 public DataContractSerializer (Type type,
113                         XmlDictionaryString rootName,
114                         XmlDictionaryString rootNamespace,
115                         IEnumerable<Type> knownTypes)
116                 {
117                         if (type == null)
118                                 throw new ArgumentNullException ("type");
119                         if (rootName == null)
120                                 throw new ArgumentNullException ("rootName");
121                         if (rootNamespace == null)
122                                 throw new ArgumentNullException ("rootNamespace");
123                         this.type = type;
124                         PopulateTypes (knownTypes);
125                         root_name = rootName;
126                         root_ns = rootNamespace;
127                 }
128
129                 public DataContractSerializer (Type type,
130                         IEnumerable<Type> knownTypes,
131                         int maxObjectsInGraph,
132                         bool ignoreExtensionDataObject,
133                         bool preserveObjectReferences,
134                         IDataContractSurrogate dataContractSurrogate)
135                         : this (type, knownTypes)
136                 {
137                         Initialize (maxObjectsInGraph,
138                                 ignoreExtensionDataObject,
139                                 preserveObjectReferences,
140                                 dataContractSurrogate);
141                 }
142
143                 public DataContractSerializer (Type type,
144                         string rootName,
145                         string rootNamespace,
146                         IEnumerable<Type> knownTypes,
147                         int maxObjectsInGraph,
148                         bool ignoreExtensionDataObject,
149                         bool preserveObjectReferences,
150                         IDataContractSurrogate dataContractSurrogate)
151                         : this (type, rootName, rootNamespace, knownTypes)
152                 {
153                         Initialize (maxObjectsInGraph,
154                                 ignoreExtensionDataObject,
155                                 preserveObjectReferences,
156                                 dataContractSurrogate);
157                 }
158
159                 public DataContractSerializer (Type type,
160                         XmlDictionaryString rootName,
161                         XmlDictionaryString rootNamespace,
162                         IEnumerable<Type> knownTypes,
163                         int maxObjectsInGraph,
164                         bool ignoreExtensionDataObject,
165                         bool preserveObjectReferences,
166                         IDataContractSurrogate dataContractSurrogate)
167                         : this (type, rootName, rootNamespace, knownTypes)
168                 {
169                         Initialize (maxObjectsInGraph,
170                                 ignoreExtensionDataObject,
171                                 preserveObjectReferences,
172                                 dataContractSurrogate);
173                 }
174
175 #if NET_4_0
176                 public DataContractSerializer (Type type,
177                         IEnumerable<Type> knownTypes,
178                         int maxObjectsInGraph,
179                         bool ignoreExtensionDataObject,
180                         bool preserveObjectReferences,
181                         IDataContractSurrogate dataContractSurrogate,
182                         DataContractResolver dataContractResolver)
183                         : this (type, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
184                 {
185                         DataContractResolver = dataContractResolver;
186                 }
187
188                 public DataContractSerializer (Type type,
189                         string rootName,
190                         string rootNamespace,
191                         IEnumerable<Type> knownTypes,
192                         int maxObjectsInGraph,
193                         bool ignoreExtensionDataObject,
194                         bool preserveObjectReferences,
195                         IDataContractSurrogate dataContractSurrogate,
196                         DataContractResolver dataContractResolver)
197                         : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
198                 {
199                         DataContractResolver = dataContractResolver;
200                 }
201
202                 public DataContractSerializer (Type type,
203                         XmlDictionaryString rootName,
204                         XmlDictionaryString rootNamespace,
205                         IEnumerable<Type> knownTypes,
206                         int maxObjectsInGraph,
207                         bool ignoreExtensionDataObject,
208                         bool preserveObjectReferences,
209                         IDataContractSurrogate dataContractSurrogate,
210                         DataContractResolver dataContractResolver)
211                         : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
212                 {
213                         DataContractResolver = dataContractResolver;
214                 }
215 #endif
216
217 #if NET_4_5
218                 public DataContractSerializer (Type type, DataContractSerializerSettings settings)
219                         : this (type, settings.RootName, settings.RootNamespace, settings.KnownTypes,
220                                 settings.MaxItemsInObjectGraph, settings.IgnoreExtensionDataObject,
221                                 settings.PreserveObjectReferences, settings.DataContractSurrogate,
222                                 settings.DataContractResolver)
223                 {
224                 }
225 #endif
226
227                 void PopulateTypes (IEnumerable<Type> knownTypes)
228                 {
229                         if (known_types == null)
230                                 known_types = new KnownTypeCollection ();
231
232                         if (specified_known_types == null)
233                                 specified_known_types = new List<Type> ();
234
235                         if (knownTypes != null) {
236                                 foreach (Type t in knownTypes) {
237                                         known_types.Add (t);
238                                         specified_known_types.Add (t);
239                                 }
240                         }
241
242                         RegisterTypeAsKnown (type);
243                 }
244
245                 void RegisterTypeAsKnown (Type type)
246                 {
247                         if (known_types.Contains (type))
248                                 return;
249
250                         Type elementType = type;
251                         if (type.HasElementType) {
252                                 known_types.Add (type);
253                                 elementType = type.GetElementType ();
254                         }
255
256                         known_types.Add (elementType);
257
258                         /* Get all KnownTypeAttribute-s, including inherited ones */
259                         object [] attrs = elementType.GetCustomAttributes (typeof (KnownTypeAttribute), true);
260                         for (int i = 0; i < attrs.Length; i ++) {
261                                 KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
262                                 foreach (var t in kt.GetTypes (elementType))
263                                         RegisterTypeAsKnown (t);
264                         }
265                 }
266
267                 void FillDictionaryString (string name, string ns)
268                 {
269                         XmlDictionary d = new XmlDictionary ();
270                         root_name = d.Add (name);
271                         root_ns = d.Add (ns);
272                         names_filled = true;
273                 }
274
275                 void Initialize (
276                         int maxObjectsInGraph,
277                         bool ignoreExtensionDataObject,
278                         bool preserveObjectReferences,
279                         IDataContractSurrogate dataContractSurrogate)
280                 {
281                         if (maxObjectsInGraph < 0)
282                                 throw new ArgumentOutOfRangeException ("maxObjectsInGraph must not be negative.");
283                         max_items = maxObjectsInGraph;
284                         ignore_ext = ignoreExtensionDataObject;
285                         preserve_refs = preserveObjectReferences;
286                         surrogate = dataContractSurrogate;
287                 }
288
289 #if NET_4_0
290                 public
291 #else
292                 internal
293 #endif
294                 DataContractResolver DataContractResolver {
295                         get { return resolver; }
296                         private set {
297                                 resolver = value;
298                                 default_resolver = default_resolver ?? new DefaultDataContractResolver (this);
299                         }
300                 }
301
302                 public bool IgnoreExtensionDataObject {
303                         get { return ignore_ext; }
304                 }
305
306                 public ReadOnlyCollection<Type> KnownTypes {
307                         get {
308                                 if (returned_known_types == null)
309                                         returned_known_types = new ReadOnlyCollection<Type> (specified_known_types);
310                                 return returned_known_types;
311                         }
312                 }
313
314                 internal KnownTypeCollection InternalKnownTypes {
315                         get { return known_types; }
316                 }
317
318                 public IDataContractSurrogate DataContractSurrogate {
319                         get { return surrogate; }
320                 }
321
322                 public int MaxItemsInObjectGraph {
323                         get { return max_items; }
324                 }
325
326                 public bool PreserveObjectReferences {
327                         get { return preserve_refs; }
328                 }
329
330                 public override bool IsStartObject (XmlDictionaryReader reader)
331                 {
332                         if (reader == null)
333                                 throw new ArgumentNullException ("reader");
334                         reader.MoveToContent ();
335                         return reader.IsStartElement (root_name, root_ns);
336                 }
337
338                 // SP1
339                 public override bool IsStartObject (XmlReader reader)
340                 {
341                         return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader));
342                 }
343
344                 // SP1
345                 public override object ReadObject (XmlReader reader)
346                 {
347                         return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader));
348                 }
349
350                 public override object ReadObject (XmlReader reader, bool verifyObjectName)
351                 {
352                         return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader), verifyObjectName);
353                 }
354
355                 public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
356                 {
357                         int startTypeCount = known_types.Count;
358                         known_types.Add (type);
359
360                         bool isEmpty = reader.IsEmptyElement;
361
362                         object ret = XmlFormatterDeserializer.Deserialize (reader, type,
363                                 known_types, surrogate, DataContractResolver, default_resolver, root_name.Value, root_ns.Value, verifyObjectName);
364
365                         // remove temporarily-added known types for
366                         // rootType and object graph type.
367                         while (known_types.Count > startTypeCount)
368                                 known_types.RemoveAt (startTypeCount);
369
370                         return ret;
371                 }
372
373 #if NET_4_0
374                 public object ReadObject (XmlDictionaryReader reader, bool verifyObjectName, DataContractResolver resolver)
375                 {
376                         var bak = DataContractResolver;
377                         try {
378                                 DataContractResolver = resolver;
379                                 return ReadObject (reader, verifyObjectName);
380                         } finally {
381                                 DataContractResolver = bak;
382                         }
383                 }
384 #endif
385
386                 // SP1
387                 public override void WriteObject (XmlWriter writer, object graph)
388                 {
389                         XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
390                         WriteObject (w, graph);
391                 }
392
393 #if NET_4_0
394                 public void WriteObject (XmlDictionaryWriter writer, object graph, DataContractResolver resolver)
395                 {
396                         var bak = DataContractResolver;
397                         try {
398                                 DataContractResolver = resolver;
399                                 WriteObject (writer, graph);
400                         } finally {
401                                 DataContractResolver = bak;
402                         }
403                 }
404 #endif
405
406                 [MonoTODO ("use DataContractSurrogate")]
407                 /*
408                         when writeContentOnly is true, then the input XmlWriter
409                         must be at element state. This is to write possible
410                         xsi:nil.
411
412                         rootType determines the top-level element QName (thus
413                         it is ignored when writeContentOnly is true).
414
415                         preserveObjectReferences indicates that whether the
416                         output should contain ms:Id or not.
417                         (http://schemas.microsoft.com/2003/10/Serialization/)
418                 */
419                 public override void WriteObjectContent (XmlDictionaryWriter writer, object graph)
420                 {
421                         if (graph == null)
422                                 return;
423
424                         int startTypeCount = known_types.Count;
425
426                         XmlFormatterSerializer.Serialize (writer, graph,
427                                 type, known_types,
428                                 ignore_ext, max_items, root_ns.Value, preserve_refs, DataContractResolver, default_resolver);
429
430                         // remove temporarily-added known types for
431                         // rootType and object graph type.
432                         while (known_types.Count > startTypeCount)
433                                 known_types.RemoveAt (startTypeCount);
434                 }
435
436                 public override void WriteObjectContent (XmlWriter writer, object graph)
437                 {
438                         XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
439                         WriteObjectContent (w, graph);
440                 }
441
442                 // SP1
443                 public override void WriteStartObject (
444                         XmlWriter writer, object graph)
445                 {
446                         WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph);
447                 }
448
449                 public override void WriteStartObject (
450                         XmlDictionaryWriter writer, object graph)
451                 {
452                         Type rootType = type;
453
454                         if (root_name.Value == "")
455                                 throw new InvalidDataContractException ("Type '" + type.ToString () +
456                                         "' cannot have a DataContract attribute Name set to null or empty string.");
457
458
459                         if (graph == null) {
460                                 if (names_filled)
461                                         writer.WriteStartElement (root_name.Value, root_ns.Value);
462                                 else
463                                         writer.WriteStartElement (root_name, root_ns);
464                                 writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
465                                 return;
466                         }
467
468                         QName rootQName = null;
469                         XmlDictionaryString name, ns;
470                         var graphType = graph.GetType ();
471                         if (DataContractResolver != null && DataContractResolver.TryResolveType (graphType, type, default_resolver, out name, out ns))
472                                 rootQName = new QName (name.Value, ns.Value);
473
474                         // 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.
475
476                         QName collectionQName;
477                         if (KnownTypeCollection.IsInterchangeableCollectionType (type, graphType, out collectionQName)) {
478                                 graphType = type;
479                                 rootQName = collectionQName;
480                         } else if (graphType != type && rootQName == null && IsUnknownType (type, graphType))
481                                 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));
482
483                         QName instName = rootQName;
484                         rootQName = rootQName ?? known_types.GetQName (rootType);
485                         QName graph_qname = known_types.GetQName (graphType);
486
487                         known_types.Add (graphType);
488
489                         if (names_filled)
490                                 writer.WriteStartElement (root_name.Value, root_ns.Value);
491                         else
492                                 writer.WriteStartElement (root_name, root_ns);
493
494                         if (rootQName != graph_qname || rootQName.Namespace != KnownTypeCollection.MSSimpleNamespace && !rootType.IsEnum)
495                                 //FIXME: Hack, when should the "i:type" be written?
496                                 //Not used in case of enums
497                                 writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
498
499                         if (root_ns.Value != rootQName.Namespace)
500                                 if (rootQName.Namespace != KnownTypeCollection.MSSimpleNamespace)
501                                         writer.WriteXmlnsAttribute (null, rootQName.Namespace);
502
503                         if (rootQName == graph_qname)
504                                 return;
505
506                         /* Different names */
507                         known_types.Add (rootType);
508
509                         instName = instName ?? KnownTypeCollection.GetPredefinedTypeName (graphType);
510                         if (instName == QName.Empty)
511                                 /* Not a primitive type */
512                                 instName = graph_qname;
513                         else
514                                 /* FIXME: Hack, .. see test WriteObject7 () */
515                                 instName = new QName (instName.Name, XmlSchema.Namespace);
516
517 /* // disabled as it now generates extraneous i:type output.
518                         // output xsi:type as rootType is not equivalent to the graph's type.
519                         writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
520                         writer.WriteQualifiedName (instName.Name, instName.Namespace);
521                         writer.WriteEndAttribute ();
522 */
523                 }
524
525                 bool IsUnknownType (Type contractType, Type type)
526                 {
527                         if (type.IsArray) {
528                                 if (KnownTypeCollection.GetAttribute<CollectionDataContractAttribute> (contractType) != null ||
529                                     KnownTypeCollection.GetAttribute<DataContractAttribute> (contractType) != null)
530                                         return true;
531                         }
532                         return IsUnknownType (type);
533                 }
534
535                 bool IsUnknownType (Type type)
536                 {
537                         if (known_types.Contains (type) ||
538                             KnownTypeCollection.GetPrimitiveTypeName (type) != QName.Empty)
539                                 return false;
540                         if (type.IsArray)
541                                 return IsUnknownType (type.GetElementType ());
542                         return true;
543                 }
544
545                 public override void WriteEndObject (XmlDictionaryWriter writer)
546                 {
547                         writer.WriteEndElement ();
548                 }
549
550                 // SP1
551                 public override void WriteEndObject (XmlWriter writer)
552                 {
553                         WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));
554                 }
555
556 #if NET_4_5
557                 [MonoTODO]
558                 public bool SerializeReadOnlyTypes {
559                         get { throw new NotImplementedException (); }
560                 }
561 #endif
562         }
563 }