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