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