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