2 // DataContractSerializer.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2005-2007 Novell, Inc. http://www.novell.com
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System.Collections;
31 using System.Collections.Generic;
32 using System.Collections.ObjectModel;
34 using System.Reflection;
35 using System.Runtime.Serialization.Formatters.Binary;
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/";
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 XmlDictionaryString root_name, root_ns;
60 public DataContractSerializer (Type type)
61 : this (type, Type.EmptyTypes)
63 // nothing to do here.
66 public DataContractSerializer (Type type,
67 IEnumerable<Type> knownTypes)
70 throw new ArgumentNullException ("type");
72 known_types = new KnownTypeCollection ();
73 QName qname = known_types.GetQName (type);
75 FillDictionaryString (qname.Name, qname.Namespace);
77 PopulateTypes (knownTypes);
80 public DataContractSerializer (Type type, string rootName,
82 : this (type, rootName, rootNamespace, Type.EmptyTypes)
84 // nothing to do here.
87 public DataContractSerializer (Type type,
88 XmlDictionaryString rootName,
89 XmlDictionaryString rootNamespace)
90 : this (type, rootName, rootNamespace, Type.EmptyTypes)
92 // nothing to do here.
95 public DataContractSerializer (Type type, string rootName,
96 string rootNamespace, IEnumerable<Type> knownTypes)
99 throw new ArgumentNullException ("type");
100 if (rootName == null)
101 throw new ArgumentNullException ("rootName");
102 if (rootNamespace == null)
103 throw new ArgumentNullException ("rootNamespace");
105 PopulateTypes (knownTypes);
106 FillDictionaryString (rootName, rootNamespace);
109 public DataContractSerializer (Type type,
110 XmlDictionaryString rootName,
111 XmlDictionaryString rootNamespace,
112 IEnumerable<Type> knownTypes)
115 throw new ArgumentNullException ("type");
116 if (rootName == null)
117 throw new ArgumentNullException ("rootName");
118 if (rootNamespace == null)
119 throw new ArgumentNullException ("rootNamespace");
121 PopulateTypes (knownTypes);
122 root_name = rootName;
123 root_ns = rootNamespace;
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)
134 Initialize (maxObjectsInGraph,
135 ignoreExtensionDataObject,
136 preserveObjectReferences,
137 dataContractSurrogate);
140 public DataContractSerializer (Type type,
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)
150 Initialize (maxObjectsInGraph,
151 ignoreExtensionDataObject,
152 preserveObjectReferences,
153 dataContractSurrogate);
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)
166 Initialize (maxObjectsInGraph,
167 ignoreExtensionDataObject,
168 preserveObjectReferences,
169 dataContractSurrogate);
172 void PopulateTypes (IEnumerable<Type> knownTypes)
174 if (known_types == null)
175 known_types= new KnownTypeCollection ();
177 if (knownTypes != null) {
178 foreach (Type t in knownTypes)
179 known_types.TryRegister (t);
182 Type elementType = type;
183 if (type.HasElementType)
184 elementType = type.GetElementType ();
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);
194 void FillDictionaryString (string name, string ns)
196 XmlDictionary d = new XmlDictionary ();
197 root_name = d.Add (name);
198 root_ns = d.Add (ns);
202 int maxObjectsInGraph,
203 bool ignoreExtensionDataObject,
204 bool preserveObjectReferences,
205 IDataContractSurrogate dataContractSurrogate)
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;
214 PopulateTypes (Type.EmptyTypes);
217 public bool IgnoreExtensionDataObject {
218 get { return ignore_ext; }
221 public ReadOnlyCollection<Type> KnownTypes {
222 get { return known_runtime_types; }
225 public IDataContractSurrogate DataContractSurrogate {
226 get { return surrogate; }
229 public int MaxItemsInObjectGraph {
230 get { return max_items; }
233 public bool PreserveObjectReferences {
234 get { return preserve_refs; }
238 public override bool IsStartObject (XmlDictionaryReader reader)
240 throw new NotImplementedException ();
244 public override bool IsStartObject (XmlReader reader)
246 return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader));
250 public override object ReadObject (XmlReader reader)
252 return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader));
256 public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
258 int startTypeCount = known_types.Count;
259 known_types.Add (type);
261 bool isEmpty = reader.IsEmptyElement;
263 object ret = XmlFormatterDeserializer.Deserialize (reader, type,
264 known_types, surrogate, root_name.Value, root_ns.Value, verifyObjectName);
266 // remove temporarily-added known types for
267 // rootType and object graph type.
268 while (known_types.Count > startTypeCount)
269 known_types.RemoveAt (startTypeCount);
274 private void ReadRootStartElement (XmlReader reader, Type type)
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?
287 public override void WriteObject (XmlWriter writer, object graph)
289 XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
290 WriteObject (w, graph);
293 [MonoTODO ("support arrays; support Serializable; support SharedType; use DataContractSurrogate")]
295 when writeContentOnly is true, then the input XmlWriter
296 must be at element state. This is to write possible
299 rootType determines the top-level element QName (thus
300 it is ignored when writeContentOnly is true).
302 preserveObjectReferences indicates that whether the
303 output should contain ms:Id or not.
304 (http://schemas.microsoft.com/2003/10/Serialization/)
306 public override void WriteObjectContent (XmlDictionaryWriter writer, object graph)
311 int startTypeCount = known_types.Count;
313 XmlFormatterSerializer.Serialize (writer, graph,
315 ignore_ext, max_items, root_ns.Value);
317 // remove temporarily-added known types for
318 // rootType and object graph type.
319 while (known_types.Count > startTypeCount)
320 known_types.RemoveAt (startTypeCount);
324 public override void WriteStartObject (
325 XmlWriter writer, object graph)
327 WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph);
330 public override void WriteStartObject (
331 XmlDictionaryWriter writer, object graph)
333 Type rootType = type;
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.");
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);
348 QName instName = null;
349 QName root_qname = known_types.GetQName (rootType);
350 QName graph_qname = known_types.GetQName (graph.GetType ());
352 known_types.Add (graph.GetType ());
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);
359 if (root_qname == graph_qname) {
360 if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace &&
362 //FIXME: Hack, when should the "i:type" be written?
363 //Not used in case of enums
364 writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
369 /* Different names */
370 known_types.Add (rootType);
372 instName = KnownTypeCollection.GetPredefinedTypeName (graph.GetType ());
373 if (instName == QName.Empty)
374 /* Not a primitive type */
375 instName = graph_qname;
377 /* FIXME: Hack, .. see test WriteObject7 () */
378 instName = new QName (instName.Name, XmlSchema.Namespace);
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 ();
386 public override void WriteEndObject (XmlDictionaryWriter writer)
388 writer.WriteEndElement ();
392 public override void WriteEndObject (XmlWriter writer)
394 WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));