2009-07-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / class / System.Runtime.Serialization / System.Runtime.Serialization / DataContractSerializer.cs
blob48aa0a29871324b7a05302a3536eb833e4fff4aa
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.
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;
39 using QName = System.Xml.XmlQualifiedName;
41 namespace System.Runtime.Serialization
43 public sealed class DataContractSerializer : XmlObjectSerializer
45 const string xmlns = "http://www.w3.org/2000/xmlns/";
47 Type type;
48 bool ignore_ext, preserve_refs;
50 // This is only for compatible mode.
51 StreamingContext context;
52 ReadOnlyCollection<Type> known_runtime_types;
53 KnownTypeCollection known_types;
54 IDataContractSurrogate surrogate;
56 int max_items = 0x10000; // FIXME: could be from config.
58 bool names_filled;
59 XmlDictionaryString root_name, root_ns;
61 public DataContractSerializer (Type type)
62 : this (type, Type.EmptyTypes)
64 // nothing to do here.
67 public DataContractSerializer (Type type,
68 IEnumerable<Type> knownTypes)
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);
78 FillDictionaryString (qname.Name, qname.Namespace);
82 public DataContractSerializer (Type type, string rootName,
83 string rootNamespace)
84 : this (type, rootName, rootNamespace, Type.EmptyTypes)
86 // nothing to do here.
89 public DataContractSerializer (Type type,
90 XmlDictionaryString rootName,
91 XmlDictionaryString rootNamespace)
92 : this (type, rootName, rootNamespace, Type.EmptyTypes)
94 // nothing to do here.
97 public DataContractSerializer (Type type, string rootName,
98 string rootNamespace, IEnumerable<Type> knownTypes)
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);
111 public DataContractSerializer (Type type,
112 XmlDictionaryString rootName,
113 XmlDictionaryString rootNamespace,
114 IEnumerable<Type> knownTypes)
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;
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)
136 Initialize (maxObjectsInGraph,
137 ignoreExtensionDataObject,
138 preserveObjectReferences,
139 dataContractSurrogate);
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)
152 Initialize (maxObjectsInGraph,
153 ignoreExtensionDataObject,
154 preserveObjectReferences,
155 dataContractSurrogate);
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)
168 Initialize (maxObjectsInGraph,
169 ignoreExtensionDataObject,
170 preserveObjectReferences,
171 dataContractSurrogate);
174 void PopulateTypes (IEnumerable<Type> knownTypes)
176 if (known_types == null)
177 known_types= new KnownTypeCollection ();
179 if (knownTypes != null) {
180 foreach (Type t in knownTypes)
181 known_types.TryRegister (t);
184 Type elementType = type;
185 if (type.HasElementType)
186 elementType = type.GetElementType ();
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);
196 void FillDictionaryString (string name, string ns)
198 XmlDictionary d = new XmlDictionary ();
199 root_name = d.Add (name);
200 root_ns = d.Add (ns);
201 names_filled = true;
204 void Initialize (
205 int maxObjectsInGraph,
206 bool ignoreExtensionDataObject,
207 bool preserveObjectReferences,
208 IDataContractSurrogate dataContractSurrogate)
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;
217 PopulateTypes (Type.EmptyTypes);
220 public bool IgnoreExtensionDataObject {
221 get { return ignore_ext; }
224 public ReadOnlyCollection<Type> KnownTypes {
225 get { return known_runtime_types; }
228 public IDataContractSurrogate DataContractSurrogate {
229 get { return surrogate; }
232 public int MaxItemsInObjectGraph {
233 get { return max_items; }
236 public bool PreserveObjectReferences {
237 get { return preserve_refs; }
240 [MonoTODO]
241 public override bool IsStartObject (XmlDictionaryReader reader)
243 throw new NotImplementedException ();
246 // SP1
247 public override bool IsStartObject (XmlReader reader)
249 return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader));
252 // SP1
253 public override object ReadObject (XmlReader reader)
255 return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader));
258 [MonoTODO]
259 public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
261 int startTypeCount = known_types.Count;
262 known_types.Add (type);
264 bool isEmpty = reader.IsEmptyElement;
266 object ret = XmlFormatterDeserializer.Deserialize (reader, type,
267 known_types, surrogate, root_name.Value, root_ns.Value, verifyObjectName);
269 // remove temporarily-added known types for
270 // rootType and object graph type.
271 while (known_types.Count > startTypeCount)
272 known_types.RemoveAt (startTypeCount);
274 return ret;
277 private void ReadRootStartElement (XmlReader reader, Type type)
279 SerializationMap map =
280 known_types.FindUserMap (type);
281 QName name = map != null ? map.XmlName :
282 KnownTypeCollection.GetPredefinedTypeName (type);
283 reader.MoveToContent ();
284 reader.ReadStartElement (name.Name, name.Namespace);
285 // FIXME: could there be any attributes to handle here?
286 reader.Read ();
289 // SP1
290 public override void WriteObject (XmlWriter writer, object graph)
292 XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
293 WriteObject (w, graph);
296 [MonoTODO ("support arrays; support Serializable; support SharedType; use DataContractSurrogate")]
298 when writeContentOnly is true, then the input XmlWriter
299 must be at element state. This is to write possible
300 xsi:nil.
302 rootType determines the top-level element QName (thus
303 it is ignored when writeContentOnly is true).
305 preserveObjectReferences indicates that whether the
306 output should contain ms:Id or not.
307 (http://schemas.microsoft.com/2003/10/Serialization/)
309 public override void WriteObjectContent (XmlDictionaryWriter writer, object graph)
311 if (graph == null)
312 return;
314 int startTypeCount = known_types.Count;
316 XmlFormatterSerializer.Serialize (writer, graph,
317 known_types,
318 ignore_ext, max_items, root_ns.Value);
320 // remove temporarily-added known types for
321 // rootType and object graph type.
322 while (known_types.Count > startTypeCount)
323 known_types.RemoveAt (startTypeCount);
326 // SP1
327 public override void WriteStartObject (
328 XmlWriter writer, object graph)
330 WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph);
333 public override void WriteStartObject (
334 XmlDictionaryWriter writer, object graph)
336 Type rootType = type;
338 if (root_name.Value == "")
339 throw new InvalidDataContractException ("Type '" + type.ToString () +
340 "' cannot have a DataContract attribute Name set to null or empty string.");
343 if (graph == null) {
344 if (names_filled)
345 writer.WriteStartElement (root_name.Value, root_ns.Value);
346 else
347 writer.WriteStartElement (root_name, root_ns);
348 writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
349 return;
352 QName instName = null;
353 QName root_qname = known_types.GetQName (rootType);
354 QName graph_qname = known_types.GetQName (graph.GetType ());
356 known_types.Add (graph.GetType ());
358 if (names_filled)
359 writer.WriteStartElement (root_name.Value, root_ns.Value);
360 else
361 writer.WriteStartElement (root_name, root_ns);
362 if (root_ns.Value != root_qname.Namespace)
363 if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace)
364 writer.WriteXmlnsAttribute (null, root_qname.Namespace);
366 if (root_qname == graph_qname) {
367 if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace &&
368 !rootType.IsEnum)
369 //FIXME: Hack, when should the "i:type" be written?
370 //Not used in case of enums
371 writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
373 return;
376 /* Different names */
377 known_types.Add (rootType);
379 instName = KnownTypeCollection.GetPredefinedTypeName (graph.GetType ());
380 if (instName == QName.Empty)
381 /* Not a primitive type */
382 instName = graph_qname;
383 else
384 /* FIXME: Hack, .. see test WriteObject7 () */
385 instName = new QName (instName.Name, XmlSchema.Namespace);
387 // output xsi:type as rootType is not equivalent to the graph's type.
388 writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
389 writer.WriteQualifiedName (instName.Name, instName.Namespace);
390 writer.WriteEndAttribute ();
393 public override void WriteEndObject (XmlDictionaryWriter writer)
395 writer.WriteEndElement ();
398 // SP1
399 public override void WriteEndObject (XmlWriter writer)
401 WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));
405 #endif