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