Add DataContractResolver stuff (dummy), and fix Id reference from inside the referenc...
[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                 IDataContractSurrogate surrogate;
55
56                 int max_items = 0x10000; // FIXME: could be from config.
57
58                 bool names_filled;
59                 XmlDictionaryString root_name, root_ns;
60
61                 public DataContractSerializer (Type type)
62                         : this (type, Type.EmptyTypes)
63                 {
64                         // nothing to do here.
65                 }
66
67                 public DataContractSerializer (Type type,
68                         IEnumerable<Type> knownTypes)
69                 {
70                         if (type == null)
71                                 throw new ArgumentNullException ("type");
72                         this.type = type;
73                         known_types = new KnownTypeCollection ();
74                         PopulateTypes (knownTypes);
75                         known_types.TryRegister (type);
76                         QName qname = known_types.GetQName (type);
77
78                         FillDictionaryString (qname.Name, qname.Namespace);
79                         
80                 }
81
82                 public DataContractSerializer (Type type, string rootName,
83                         string rootNamespace)
84                         : this (type, rootName, rootNamespace, Type.EmptyTypes)
85                 {
86                         // nothing to do here.
87                 }
88
89                 public DataContractSerializer (Type type,
90                         XmlDictionaryString rootName,
91                         XmlDictionaryString rootNamespace)
92                         : this (type, rootName, rootNamespace, Type.EmptyTypes)
93                 {
94                         // nothing to do here.
95                 }
96
97                 public DataContractSerializer (Type type, string rootName,
98                         string rootNamespace, IEnumerable<Type> knownTypes)
99                 {
100                         if (type == null)
101                                 throw new ArgumentNullException ("type");
102                         if (rootName == null)
103                                 throw new ArgumentNullException ("rootName");
104                         if (rootNamespace == null)
105                                 throw new ArgumentNullException ("rootNamespace");
106                         this.type = type;
107                         PopulateTypes (knownTypes);
108                         FillDictionaryString (rootName, rootNamespace);
109                 }
110
111                 public DataContractSerializer (Type type,
112                         XmlDictionaryString rootName,
113                         XmlDictionaryString rootNamespace,
114                         IEnumerable<Type> knownTypes)
115                 {
116                         if (type == null)
117                                 throw new ArgumentNullException ("type");
118                         if (rootName == null)
119                                 throw new ArgumentNullException ("rootName");
120                         if (rootNamespace == null)
121                                 throw new ArgumentNullException ("rootNamespace");
122                         this.type = type;
123                         PopulateTypes (knownTypes);
124                         root_name = rootName;
125                         root_ns = rootNamespace;
126                 }
127
128                 public DataContractSerializer (Type type,
129                         IEnumerable<Type> knownTypes,
130                         int maxObjectsInGraph,
131                         bool ignoreExtensionDataObject,
132                         bool preserveObjectReferences,
133                         IDataContractSurrogate dataContractSurrogate)
134                         : this (type, knownTypes)
135                 {
136                         Initialize (maxObjectsInGraph,
137                                 ignoreExtensionDataObject,
138                                 preserveObjectReferences,
139                                 dataContractSurrogate);
140                 }
141
142                 public DataContractSerializer (Type type,
143                         string rootName,
144                         string rootNamespace,
145                         IEnumerable<Type> knownTypes,
146                         int maxObjectsInGraph,
147                         bool ignoreExtensionDataObject,
148                         bool preserveObjectReferences,
149                         IDataContractSurrogate dataContractSurrogate)
150                         : this (type, rootName, rootNamespace, knownTypes)
151                 {
152                         Initialize (maxObjectsInGraph,
153                                 ignoreExtensionDataObject,
154                                 preserveObjectReferences,
155                                 dataContractSurrogate);
156                 }
157
158                 public DataContractSerializer (Type type,
159                         XmlDictionaryString rootName,
160                         XmlDictionaryString rootNamespace,
161                         IEnumerable<Type> knownTypes,
162                         int maxObjectsInGraph,
163                         bool ignoreExtensionDataObject,
164                         bool preserveObjectReferences,
165                         IDataContractSurrogate dataContractSurrogate)
166                         : this (type, rootName, rootNamespace, knownTypes)
167                 {
168                         Initialize (maxObjectsInGraph,
169                                 ignoreExtensionDataObject,
170                                 preserveObjectReferences,
171                                 dataContractSurrogate);
172                 }
173
174 #if NET_4_0
175                 public DataContractSerializer (Type type,
176                         IEnumerable<Type> knownTypes,
177                         int maxObjectsInGraph,
178                         bool ignoreExtensionDataObject,
179                         bool preserveObjectReferences,
180                         IDataContractSurrogate dataContractSurrogate,
181                         DataContractResolver dataContractResolver)
182                         : this (type, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
183                 {
184                         DataContractResolver = dataContractResolver;
185                 }
186
187                 public DataContractSerializer (Type type,
188                         string rootName,
189                         string rootNamespace,
190                         IEnumerable<Type> knownTypes,
191                         int maxObjectsInGraph,
192                         bool ignoreExtensionDataObject,
193                         bool preserveObjectReferences,
194                         IDataContractSurrogate dataContractSurrogate,
195                         DataContractResolver dataContractResolver)
196                         : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
197                 {
198                         DataContractResolver = dataContractResolver;
199                 }
200
201                 public DataContractSerializer (Type type,
202                         XmlDictionaryString rootName,
203                         XmlDictionaryString rootNamespace,
204                         IEnumerable<Type> knownTypes,
205                         int maxObjectsInGraph,
206                         bool ignoreExtensionDataObject,
207                         bool preserveObjectReferences,
208                         IDataContractSurrogate dataContractSurrogate,
209                         DataContractResolver dataContractResolver)
210                         : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
211                 {
212                         DataContractResolver = dataContractResolver;
213                 }
214 #endif
215
216                 void PopulateTypes (IEnumerable<Type> knownTypes)
217                 {
218                         if (known_types == null)
219                                 known_types= new KnownTypeCollection ();
220
221                         if (knownTypes != null) {
222                                 foreach (Type t in knownTypes)
223                                         known_types.TryRegister (t);
224                         }
225
226                         Type elementType = type;
227                         if (type.HasElementType)
228                                 elementType = type.GetElementType ();
229
230                         /* Get all KnownTypeAttribute-s, including inherited ones */
231                         object [] attrs = elementType.GetCustomAttributes (typeof (KnownTypeAttribute), true);
232                         for (int i = 0; i < attrs.Length; i ++) {
233                                 KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
234                                 known_types.TryRegister (kt.Type);
235                         }
236                 }
237
238                 void FillDictionaryString (string name, string ns)
239                 {
240                         XmlDictionary d = new XmlDictionary ();
241                         root_name = d.Add (name);
242                         root_ns = d.Add (ns);
243                         names_filled = true;
244                 }
245
246                 void Initialize (
247                         int maxObjectsInGraph,
248                         bool ignoreExtensionDataObject,
249                         bool preserveObjectReferences,
250                         IDataContractSurrogate dataContractSurrogate)
251                 {
252                         if (maxObjectsInGraph < 0)
253                                 throw new ArgumentOutOfRangeException ("maxObjectsInGraph must not be negative.");
254                         max_items = maxObjectsInGraph;
255                         ignore_ext = ignoreExtensionDataObject;
256                         preserve_refs = preserveObjectReferences;
257                         surrogate = dataContractSurrogate;
258
259                         PopulateTypes (Type.EmptyTypes);
260                 }
261
262 #if NET_4_0
263                 public
264 #else
265                 internal
266 #endif
267                 DataContractResolver DataContractResolver { get; private set; }
268
269                 public bool IgnoreExtensionDataObject {
270                         get { return ignore_ext; }
271                 }
272
273                 public ReadOnlyCollection<Type> KnownTypes {
274                         get {
275                                 if (returned_known_types == null)
276                                         returned_known_types = new ReadOnlyCollection<Type> (known_types);
277                                 return returned_known_types;
278                         }
279                 }
280
281                 public IDataContractSurrogate DataContractSurrogate {
282                         get { return surrogate; }
283                 }
284
285                 public int MaxItemsInObjectGraph {
286                         get { return max_items; }
287                 }
288
289                 public bool PreserveObjectReferences {
290                         get { return preserve_refs; }
291                 }
292
293                 public override bool IsStartObject (XmlDictionaryReader reader)
294                 {
295                         if (reader == null)
296                                 throw new ArgumentNullException ("reader");
297                         reader.MoveToContent ();
298                         return reader.IsStartElement (root_name, root_ns);
299                 }
300
301                 // SP1
302                 public override bool IsStartObject (XmlReader reader)
303                 {
304                         return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader));
305                 }
306
307                 // SP1
308                 public override object ReadObject (XmlReader reader)
309                 {
310                         return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader));
311                 }
312
313                 public override object ReadObject (XmlReader reader, bool verifyObjectName)
314                 {
315                         return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader), verifyObjectName);
316                 }
317
318                 public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
319                 {
320                         int startTypeCount = known_types.Count;
321                         known_types.Add (type);
322
323                         bool isEmpty = reader.IsEmptyElement;
324
325                         object ret = XmlFormatterDeserializer.Deserialize (reader, type,
326                                 known_types, surrogate, DataContractResolver, root_name.Value, root_ns.Value, verifyObjectName);
327
328                         // remove temporarily-added known types for
329                         // rootType and object graph type.
330                         while (known_types.Count > startTypeCount)
331                                 known_types.RemoveAt (startTypeCount);
332
333                         return ret;
334                 }
335
336 #if NET_4_0
337                 public object ReadObject (XmlDictionaryReader reader, bool verifyObjectName, DataContractResolver resolver)
338                 {
339                         var bak = DataContractResolver;
340                         try {
341                                 DataContractResolver = resolver;
342                                 return ReadObject (reader, verifyObjectName);
343                         } finally {
344                                 DataContractResolver = bak;
345                         }
346                 }
347 #endif
348
349                 private void ReadRootStartElement (XmlReader reader, Type type)
350                 {
351                         SerializationMap map =
352                                 known_types.FindUserMap (type);
353                         QName name = map != null ? map.XmlName :
354                                 KnownTypeCollection.GetPredefinedTypeName (type);
355                         reader.MoveToContent ();
356                         reader.ReadStartElement (name.Name, name.Namespace);
357                         // FIXME: could there be any attributes to handle here?
358                         reader.Read ();
359                 }
360
361                 // SP1
362                 public override void WriteObject (XmlWriter writer, object graph)
363                 {
364                         XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
365                         WriteObject (w, graph);
366                 }
367
368 #if NET_4_0
369                 public void WriteObject (XmlDictionaryWriter writer, object graph, DataContractResolver resolver)
370                 {
371                         var bak = DataContractResolver;
372                         try {
373                                 DataContractResolver = resolver;
374                                 WriteObject (writer, graph);
375                         } finally {
376                                 DataContractResolver = bak;
377                         }
378                 }
379 #endif
380
381                 [MonoTODO ("support arrays; support Serializable; support SharedType; use DataContractSurrogate")]
382                 /*
383                         when writeContentOnly is true, then the input XmlWriter
384                         must be at element state. This is to write possible
385                         xsi:nil.
386
387                         rootType determines the top-level element QName (thus
388                         it is ignored when writeContentOnly is true).
389
390                         preserveObjectReferences indicates that whether the
391                         output should contain ms:Id or not.
392                         (http://schemas.microsoft.com/2003/10/Serialization/)
393                 */
394                 public override void WriteObjectContent (XmlDictionaryWriter writer, object graph)
395                 {
396                         if (graph == null)
397                                 return;
398
399                         int startTypeCount = known_types.Count;
400
401                         XmlFormatterSerializer.Serialize (writer, graph,
402                                 known_types,
403                                 ignore_ext, max_items, root_ns.Value, preserve_refs);
404
405                         // remove temporarily-added known types for
406                         // rootType and object graph type.
407                         while (known_types.Count > startTypeCount)
408                                 known_types.RemoveAt (startTypeCount);
409                 }
410
411                 public override void WriteObjectContent (XmlWriter writer, object graph)
412                 {
413                         XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
414                         WriteObjectContent (w, graph);
415                 }
416
417                 // SP1
418                 public override void WriteStartObject (
419                         XmlWriter writer, object graph)
420                 {
421                         WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph);
422                 }
423
424                 public override void WriteStartObject (
425                         XmlDictionaryWriter writer, object graph)
426                 {
427                         Type rootType = type;
428                         
429                         if (root_name.Value == "")
430                                 throw new InvalidDataContractException ("Type '" + type.ToString () +
431                                         "' cannot have a DataContract attribute Name set to null or empty string.");
432
433
434                         if (graph == null) {
435                                 if (names_filled)
436                                         writer.WriteStartElement (root_name.Value, root_ns.Value);
437                                 else
438                                         writer.WriteStartElement (root_name, root_ns);
439                                 writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
440                                 return;
441                         }
442
443                         QName instName = null;
444                         QName root_qname = known_types.GetQName (rootType);
445                         QName graph_qname = known_types.GetQName (graph.GetType ());
446
447                         known_types.Add (graph.GetType ());
448
449                         if (names_filled)
450                                 writer.WriteStartElement (root_name.Value, root_ns.Value);
451                         else
452                                 writer.WriteStartElement (root_name, root_ns);
453                         if (root_ns.Value != root_qname.Namespace)
454                                 if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace)
455                                         writer.WriteXmlnsAttribute (null, root_qname.Namespace);
456
457                         if (root_qname == graph_qname) {
458                                 if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace &&
459                                         !rootType.IsEnum)
460                                         //FIXME: Hack, when should the "i:type" be written?
461                                         //Not used in case of enums
462                                         writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
463
464                                 return;
465                         }
466
467                         /* Different names */
468                         known_types.Add (rootType);
469                         
470                         instName = KnownTypeCollection.GetPredefinedTypeName (graph.GetType ());
471                         if (instName == QName.Empty)
472                                 /* Not a primitive type */
473                                 instName = graph_qname;
474                         else
475                                 /* FIXME: Hack, .. see test WriteObject7 () */
476                                 instName = new QName (instName.Name, XmlSchema.Namespace);
477
478                         // output xsi:type as rootType is not equivalent to the graph's type.
479                         writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
480                         writer.WriteQualifiedName (instName.Name, instName.Namespace);
481                         writer.WriteEndAttribute ();
482                 }
483
484                 public override void WriteEndObject (XmlDictionaryWriter writer)
485                 {
486                         writer.WriteEndElement ();
487                 }
488
489                 // SP1
490                 public override void WriteEndObject (XmlWriter writer)
491                 {
492                         WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));
493                 }
494         }
495 }
496 #endif