Merge branch 'master' of github.com:mono/mono
[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                 void PopulateTypes (IEnumerable<Type> knownTypes)
175                 {
176                         if (known_types == null)
177                                 known_types= new KnownTypeCollection ();
178
179                         if (knownTypes != null) {
180                                 foreach (Type t in knownTypes)
181                                         known_types.TryRegister (t);
182                         }
183
184                         Type elementType = type;
185                         if (type.HasElementType)
186                                 elementType = type.GetElementType ();
187
188                         /* Get all KnownTypeAttribute-s, including inherited ones */
189                         object [] attrs = elementType.GetCustomAttributes (typeof (KnownTypeAttribute), true);
190                         for (int i = 0; i < attrs.Length; i ++) {
191                                 KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
192                                 known_types.TryRegister (kt.Type);
193                         }
194                 }
195
196                 void FillDictionaryString (string name, string ns)
197                 {
198                         XmlDictionary d = new XmlDictionary ();
199                         root_name = d.Add (name);
200                         root_ns = d.Add (ns);
201                         names_filled = true;
202                 }
203
204                 void Initialize (
205                         int maxObjectsInGraph,
206                         bool ignoreExtensionDataObject,
207                         bool preserveObjectReferences,
208                         IDataContractSurrogate dataContractSurrogate)
209                 {
210                         if (maxObjectsInGraph < 0)
211                                 throw new ArgumentOutOfRangeException ("maxObjectsInGraph must not be negative.");
212                         max_items = maxObjectsInGraph;
213                         ignore_ext = ignoreExtensionDataObject;
214                         preserve_refs = preserveObjectReferences;
215                         surrogate = dataContractSurrogate;
216
217                         PopulateTypes (Type.EmptyTypes);
218                 }
219
220                 public bool IgnoreExtensionDataObject {
221                         get { return ignore_ext; }
222                 }
223
224                 public ReadOnlyCollection<Type> KnownTypes {
225                         get {
226                                 if (returned_known_types == null)
227                                         returned_known_types = new ReadOnlyCollection<Type> (known_types);
228                                 return returned_known_types;
229                         }
230                 }
231
232                 public IDataContractSurrogate DataContractSurrogate {
233                         get { return surrogate; }
234                 }
235
236                 public int MaxItemsInObjectGraph {
237                         get { return max_items; }
238                 }
239
240                 public bool PreserveObjectReferences {
241                         get { return preserve_refs; }
242                 }
243
244                 public override bool IsStartObject (XmlDictionaryReader reader)
245                 {
246                         if (reader == null)
247                                 throw new ArgumentNullException ("reader");
248                         reader.MoveToContent ();
249                         return reader.IsStartElement (root_name, root_ns);
250                 }
251
252                 // SP1
253                 public override bool IsStartObject (XmlReader reader)
254                 {
255                         return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader));
256                 }
257
258                 // SP1
259                 public override object ReadObject (XmlReader reader)
260                 {
261                         return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader));
262                 }
263
264                 public override object ReadObject (XmlReader reader, bool verifyObjectName)
265                 {
266                         return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader), verifyObjectName);
267                 }
268
269                 [MonoTODO]
270                 public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
271                 {
272                         int startTypeCount = known_types.Count;
273                         known_types.Add (type);
274
275                         bool isEmpty = reader.IsEmptyElement;
276
277                         object ret = XmlFormatterDeserializer.Deserialize (reader, type,
278                                 known_types, surrogate, root_name.Value, root_ns.Value, verifyObjectName);
279
280                         // remove temporarily-added known types for
281                         // rootType and object graph type.
282                         while (known_types.Count > startTypeCount)
283                                 known_types.RemoveAt (startTypeCount);
284
285                         return ret;
286                 }
287
288                 private void ReadRootStartElement (XmlReader reader, Type type)
289                 {
290                         SerializationMap map =
291                                 known_types.FindUserMap (type);
292                         QName name = map != null ? map.XmlName :
293                                 KnownTypeCollection.GetPredefinedTypeName (type);
294                         reader.MoveToContent ();
295                         reader.ReadStartElement (name.Name, name.Namespace);
296                         // FIXME: could there be any attributes to handle here?
297                         reader.Read ();
298                 }
299
300                 // SP1
301                 public override void WriteObject (XmlWriter writer, object graph)
302                 {
303                         XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
304                         WriteObject (w, graph);
305                 }
306
307                 [MonoTODO ("support arrays; support Serializable; support SharedType; use DataContractSurrogate")]
308                 /*
309                         when writeContentOnly is true, then the input XmlWriter
310                         must be at element state. This is to write possible
311                         xsi:nil.
312
313                         rootType determines the top-level element QName (thus
314                         it is ignored when writeContentOnly is true).
315
316                         preserveObjectReferences indicates that whether the
317                         output should contain ms:Id or not.
318                         (http://schemas.microsoft.com/2003/10/Serialization/)
319                 */
320                 public override void WriteObjectContent (XmlDictionaryWriter writer, object graph)
321                 {
322                         if (graph == null)
323                                 return;
324
325                         int startTypeCount = known_types.Count;
326
327                         XmlFormatterSerializer.Serialize (writer, graph,
328                                 known_types,
329                                 ignore_ext, max_items, root_ns.Value, preserve_refs);
330
331                         // remove temporarily-added known types for
332                         // rootType and object graph type.
333                         while (known_types.Count > startTypeCount)
334                                 known_types.RemoveAt (startTypeCount);
335                 }
336
337                 public override void WriteObjectContent (XmlWriter writer, object graph)
338                 {
339                         XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
340                         WriteObjectContent (w, graph);
341                 }
342
343                 // SP1
344                 public override void WriteStartObject (
345                         XmlWriter writer, object graph)
346                 {
347                         WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph);
348                 }
349
350                 public override void WriteStartObject (
351                         XmlDictionaryWriter writer, object graph)
352                 {
353                         Type rootType = type;
354                         
355                         if (root_name.Value == "")
356                                 throw new InvalidDataContractException ("Type '" + type.ToString () +
357                                         "' cannot have a DataContract attribute Name set to null or empty string.");
358
359
360                         if (graph == null) {
361                                 if (names_filled)
362                                         writer.WriteStartElement (root_name.Value, root_ns.Value);
363                                 else
364                                         writer.WriteStartElement (root_name, root_ns);
365                                 writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
366                                 return;
367                         }
368
369                         QName instName = null;
370                         QName root_qname = known_types.GetQName (rootType);
371                         QName graph_qname = known_types.GetQName (graph.GetType ());
372
373                         known_types.Add (graph.GetType ());
374
375                         if (names_filled)
376                                 writer.WriteStartElement (root_name.Value, root_ns.Value);
377                         else
378                                 writer.WriteStartElement (root_name, root_ns);
379                         if (root_ns.Value != root_qname.Namespace)
380                                 if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace)
381                                         writer.WriteXmlnsAttribute (null, root_qname.Namespace);
382
383                         if (root_qname == graph_qname) {
384                                 if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace &&
385                                         !rootType.IsEnum)
386                                         //FIXME: Hack, when should the "i:type" be written?
387                                         //Not used in case of enums
388                                         writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
389
390                                 return;
391                         }
392
393                         /* Different names */
394                         known_types.Add (rootType);
395                         
396                         instName = KnownTypeCollection.GetPredefinedTypeName (graph.GetType ());
397                         if (instName == QName.Empty)
398                                 /* Not a primitive type */
399                                 instName = graph_qname;
400                         else
401                                 /* FIXME: Hack, .. see test WriteObject7 () */
402                                 instName = new QName (instName.Name, XmlSchema.Namespace);
403
404                         // output xsi:type as rootType is not equivalent to the graph's type.
405                         writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
406                         writer.WriteQualifiedName (instName.Name, instName.Namespace);
407                         writer.WriteEndAttribute ();
408                 }
409
410                 public override void WriteEndObject (XmlDictionaryWriter writer)
411                 {
412                         writer.WriteEndElement ();
413                 }
414
415                 // SP1
416                 public override void WriteEndObject (XmlWriter writer)
417                 {
418                         WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));
419                 }
420         }
421 }
422 #endif