Add unit test for AggregateException.GetBaseException that works on .net but is broke...
[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                                 elementType = type.GetElementType ();
253
254                         known_types.Add (elementType);
255
256                         /* Get all KnownTypeAttribute-s, including inherited ones */
257                         object [] attrs = elementType.GetCustomAttributes (typeof (KnownTypeAttribute), true);
258                         for (int i = 0; i < attrs.Length; i ++) {
259                                 KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
260                                 foreach (var t in kt.GetTypes (elementType))
261                                         RegisterTypeAsKnown (t);
262                         }
263                 }
264
265                 void FillDictionaryString (string name, string ns)
266                 {
267                         XmlDictionary d = new XmlDictionary ();
268                         root_name = d.Add (name);
269                         root_ns = d.Add (ns);
270                         names_filled = true;
271                 }
272
273                 void Initialize (
274                         int maxObjectsInGraph,
275                         bool ignoreExtensionDataObject,
276                         bool preserveObjectReferences,
277                         IDataContractSurrogate dataContractSurrogate)
278                 {
279                         if (maxObjectsInGraph < 0)
280                                 throw new ArgumentOutOfRangeException ("maxObjectsInGraph must not be negative.");
281                         max_items = maxObjectsInGraph;
282                         ignore_ext = ignoreExtensionDataObject;
283                         preserve_refs = preserveObjectReferences;
284                         surrogate = dataContractSurrogate;
285                 }
286
287 #if NET_4_0
288                 public
289 #else
290                 internal
291 #endif
292                 DataContractResolver DataContractResolver {
293                         get { return resolver; }
294                         private set {
295                                 resolver = value;
296                                 default_resolver = default_resolver ?? new DefaultDataContractResolver (this);
297                         }
298                 }
299
300                 public bool IgnoreExtensionDataObject {
301                         get { return ignore_ext; }
302                 }
303
304                 public ReadOnlyCollection<Type> KnownTypes {
305                         get {
306                                 if (returned_known_types == null)
307                                         returned_known_types = new ReadOnlyCollection<Type> (specified_known_types);
308                                 return returned_known_types;
309                         }
310                 }
311
312                 internal KnownTypeCollection InternalKnownTypes {
313                         get { return known_types; }
314                 }
315
316                 public IDataContractSurrogate DataContractSurrogate {
317                         get { return surrogate; }
318                 }
319
320                 public int MaxItemsInObjectGraph {
321                         get { return max_items; }
322                 }
323
324                 public bool PreserveObjectReferences {
325                         get { return preserve_refs; }
326                 }
327
328                 public override bool IsStartObject (XmlDictionaryReader reader)
329                 {
330                         if (reader == null)
331                                 throw new ArgumentNullException ("reader");
332                         reader.MoveToContent ();
333                         return reader.IsStartElement (root_name, root_ns);
334                 }
335
336                 // SP1
337                 public override bool IsStartObject (XmlReader reader)
338                 {
339                         return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader));
340                 }
341
342                 // SP1
343                 public override object ReadObject (XmlReader reader)
344                 {
345                         return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader));
346                 }
347
348                 public override object ReadObject (XmlReader reader, bool verifyObjectName)
349                 {
350                         return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader), verifyObjectName);
351                 }
352
353                 public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
354                 {
355                         int startTypeCount = known_types.Count;
356                         known_types.Add (type);
357
358                         bool isEmpty = reader.IsEmptyElement;
359
360                         object ret = XmlFormatterDeserializer.Deserialize (reader, type,
361                                 known_types, surrogate, DataContractResolver, default_resolver, root_name.Value, root_ns.Value, verifyObjectName);
362
363                         // remove temporarily-added known types for
364                         // rootType and object graph type.
365                         while (known_types.Count > startTypeCount)
366                                 known_types.RemoveAt (startTypeCount);
367
368                         return ret;
369                 }
370
371 #if NET_4_0
372                 public object ReadObject (XmlDictionaryReader reader, bool verifyObjectName, DataContractResolver resolver)
373                 {
374                         var bak = DataContractResolver;
375                         try {
376                                 DataContractResolver = resolver;
377                                 return ReadObject (reader, verifyObjectName);
378                         } finally {
379                                 DataContractResolver = bak;
380                         }
381                 }
382 #endif
383
384                 // SP1
385                 public override void WriteObject (XmlWriter writer, object graph)
386                 {
387                         XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
388                         WriteObject (w, graph);
389                 }
390
391 #if NET_4_0
392                 public void WriteObject (XmlDictionaryWriter writer, object graph, DataContractResolver resolver)
393                 {
394                         var bak = DataContractResolver;
395                         try {
396                                 DataContractResolver = resolver;
397                                 WriteObject (writer, graph);
398                         } finally {
399                                 DataContractResolver = bak;
400                         }
401                 }
402 #endif
403
404                 [MonoTODO ("use DataContractSurrogate")]
405                 /*
406                         when writeContentOnly is true, then the input XmlWriter
407                         must be at element state. This is to write possible
408                         xsi:nil.
409
410                         rootType determines the top-level element QName (thus
411                         it is ignored when writeContentOnly is true).
412
413                         preserveObjectReferences indicates that whether the
414                         output should contain ms:Id or not.
415                         (http://schemas.microsoft.com/2003/10/Serialization/)
416                 */
417                 public override void WriteObjectContent (XmlDictionaryWriter writer, object graph)
418                 {
419                         if (graph == null)
420                                 return;
421
422                         int startTypeCount = known_types.Count;
423
424                         XmlFormatterSerializer.Serialize (writer, graph,
425                                 type, known_types,
426                                 ignore_ext, max_items, root_ns.Value, preserve_refs, DataContractResolver, default_resolver);
427
428                         // remove temporarily-added known types for
429                         // rootType and object graph type.
430                         while (known_types.Count > startTypeCount)
431                                 known_types.RemoveAt (startTypeCount);
432                 }
433
434                 public override void WriteObjectContent (XmlWriter writer, object graph)
435                 {
436                         XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
437                         WriteObjectContent (w, graph);
438                 }
439
440                 // SP1
441                 public override void WriteStartObject (
442                         XmlWriter writer, object graph)
443                 {
444                         WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph);
445                 }
446
447                 public override void WriteStartObject (
448                         XmlDictionaryWriter writer, object graph)
449                 {
450                         Type rootType = type;
451
452                         if (root_name.Value == "")
453                                 throw new InvalidDataContractException ("Type '" + type.ToString () +
454                                         "' cannot have a DataContract attribute Name set to null or empty string.");
455
456
457                         if (graph == null) {
458                                 if (names_filled)
459                                         writer.WriteStartElement (root_name.Value, root_ns.Value);
460                                 else
461                                         writer.WriteStartElement (root_name, root_ns);
462                                 writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
463                                 return;
464                         }
465
466                         QName rootQName = null;
467                         XmlDictionaryString name, ns;
468                         var graphType = graph.GetType ();
469                         if (DataContractResolver != null && DataContractResolver.TryResolveType (graphType, type, default_resolver, out name, out ns))
470                                 rootQName = new QName (name.Value, ns.Value);
471
472                         // 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.
473
474                         QName collectionQName;
475                         if (KnownTypeCollection.IsInterchangeableCollectionType (type, graphType, out collectionQName)) {
476                                 graphType = type;
477                                 rootQName = collectionQName;
478                         } else if (graphType != type && rootQName == null && IsUnknownType (type, graphType))
479                                 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));
480
481                         QName instName = rootQName;
482                         rootQName = rootQName ?? known_types.GetQName (rootType);
483                         QName graph_qname = known_types.GetQName (graphType);
484
485                         known_types.Add (graphType);
486
487                         if (names_filled)
488                                 writer.WriteStartElement (root_name.Value, root_ns.Value);
489                         else
490                                 writer.WriteStartElement (root_name, root_ns);
491
492                         if (rootQName != graph_qname || rootQName.Namespace != KnownTypeCollection.MSSimpleNamespace && !rootType.IsEnum)
493                                 //FIXME: Hack, when should the "i:type" be written?
494                                 //Not used in case of enums
495                                 writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
496
497                         if (root_ns.Value != rootQName.Namespace)
498                                 if (rootQName.Namespace != KnownTypeCollection.MSSimpleNamespace)
499                                         writer.WriteXmlnsAttribute (null, rootQName.Namespace);
500
501                         if (rootQName == graph_qname)
502                                 return;
503
504                         /* Different names */
505                         known_types.Add (rootType);
506
507                         instName = instName ?? KnownTypeCollection.GetPredefinedTypeName (graphType);
508                         if (instName == QName.Empty)
509                                 /* Not a primitive type */
510                                 instName = graph_qname;
511                         else
512                                 /* FIXME: Hack, .. see test WriteObject7 () */
513                                 instName = new QName (instName.Name, XmlSchema.Namespace);
514
515 /* // disabled as it now generates extraneous i:type output.
516                         // output xsi:type as rootType is not equivalent to the graph's type.
517                         writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
518                         writer.WriteQualifiedName (instName.Name, instName.Namespace);
519                         writer.WriteEndAttribute ();
520 */
521                 }
522
523                 bool IsUnknownType (Type contractType, Type type)
524                 {
525                         if (type.IsArray) {
526                                 if (KnownTypeCollection.GetAttribute<CollectionDataContractAttribute> (contractType) != null ||
527                                     KnownTypeCollection.GetAttribute<DataContractAttribute> (contractType) != null)
528                                         return true;
529                         }
530                         return IsUnknownType (type);
531                 }
532
533                 bool IsUnknownType (Type type)
534                 {
535                         if (known_types.Contains (type) ||
536                             KnownTypeCollection.GetPrimitiveTypeName (type) != QName.Empty)
537                                 return false;
538                         if (type.IsArray)
539                                 return IsUnknownType (type.GetElementType ());
540                         return true;
541                 }
542
543                 public override void WriteEndObject (XmlDictionaryWriter writer)
544                 {
545                         writer.WriteEndElement ();
546                 }
547
548                 // SP1
549                 public override void WriteEndObject (XmlWriter writer)
550                 {
551                         WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));
552                 }
553
554 #if NET_4_5
555                 [MonoTODO]
556                 public bool SerializeReadOnlyTypes {
557                         get { throw new NotImplementedException (); }
558                 }
559 #endif
560         }
561 }