New tests.
[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                 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 { return known_runtime_types; }
226                 }
227
228                 public IDataContractSurrogate DataContractSurrogate {
229                         get { return surrogate; }
230                 }
231
232                 public int MaxItemsInObjectGraph {
233                         get { return max_items; }
234                 }
235
236                 public bool PreserveObjectReferences {
237                         get { return preserve_refs; }
238                 }
239
240                 public override bool IsStartObject (XmlDictionaryReader reader)
241                 {
242                         if (reader == null)
243                                 throw new ArgumentNullException ("reader");
244                         reader.MoveToContent ();
245                         return reader.IsStartElement (root_name, root_ns);
246                 }
247
248                 // SP1
249                 public override bool IsStartObject (XmlReader reader)
250                 {
251                         return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader));
252                 }
253
254                 // SP1
255                 public override object ReadObject (XmlReader reader)
256                 {
257                         return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader));
258                 }
259
260                 public override object ReadObject (XmlReader reader, bool verifyObjectName)
261                 {
262                         return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader), verifyObjectName);
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                 public override void WriteObjectContent (XmlWriter writer, object graph)
334                 {
335                         XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
336                         WriteObjectContent (w, graph);
337                 }
338
339                 // SP1
340                 public override void WriteStartObject (
341                         XmlWriter writer, object graph)
342                 {
343                         WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph);
344                 }
345
346                 public override void WriteStartObject (
347                         XmlDictionaryWriter writer, object graph)
348                 {
349                         Type rootType = type;
350                         
351                         if (root_name.Value == "")
352                                 throw new InvalidDataContractException ("Type '" + type.ToString () +
353                                         "' cannot have a DataContract attribute Name set to null or empty string.");
354
355
356                         if (graph == null) {
357                                 if (names_filled)
358                                         writer.WriteStartElement (root_name.Value, root_ns.Value);
359                                 else
360                                         writer.WriteStartElement (root_name, root_ns);
361                                 writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
362                                 return;
363                         }
364
365                         QName instName = null;
366                         QName root_qname = known_types.GetQName (rootType);
367                         QName graph_qname = known_types.GetQName (graph.GetType ());
368
369                         known_types.Add (graph.GetType ());
370
371                         if (names_filled)
372                                 writer.WriteStartElement (root_name.Value, root_ns.Value);
373                         else
374                                 writer.WriteStartElement (root_name, root_ns);
375                         if (root_ns.Value != root_qname.Namespace)
376                                 if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace)
377                                         writer.WriteXmlnsAttribute (null, root_qname.Namespace);
378
379                         if (root_qname == graph_qname) {
380                                 if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace &&
381                                         !rootType.IsEnum)
382                                         //FIXME: Hack, when should the "i:type" be written?
383                                         //Not used in case of enums
384                                         writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
385
386                                 return;
387                         }
388
389                         /* Different names */
390                         known_types.Add (rootType);
391                         
392                         instName = KnownTypeCollection.GetPredefinedTypeName (graph.GetType ());
393                         if (instName == QName.Empty)
394                                 /* Not a primitive type */
395                                 instName = graph_qname;
396                         else
397                                 /* FIXME: Hack, .. see test WriteObject7 () */
398                                 instName = new QName (instName.Name, XmlSchema.Namespace);
399
400                         // output xsi:type as rootType is not equivalent to the graph's type.
401                         writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
402                         writer.WriteQualifiedName (instName.Name, instName.Namespace);
403                         writer.WriteEndAttribute ();
404                 }
405
406                 public override void WriteEndObject (XmlDictionaryWriter writer)
407                 {
408                         writer.WriteEndElement ();
409                 }
410
411                 // SP1
412                 public override void WriteEndObject (XmlWriter writer)
413                 {
414                         WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));
415                 }
416         }
417 }
418 #endif