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> returned_known_types;
53 KnownTypeCollection known_types;
54 List<Type> specified_known_types;
55 IDataContractSurrogate surrogate;
56 DataContractResolver resolver, default_resolver;
58 int max_items = 0x10000; // FIXME: could be from config.
61 XmlDictionaryString root_name, root_ns;
63 public DataContractSerializer (Type type)
64 : this (type, Type.EmptyTypes)
66 // nothing to do here.
69 public DataContractSerializer (Type type,
70 IEnumerable<Type> knownTypes)
73 throw new ArgumentNullException ("type");
75 PopulateTypes (knownTypes);
76 known_types.Add (type);
77 QName qname = known_types.GetQName (type);
79 FillDictionaryString (qname.Name, qname.Namespace);
83 public DataContractSerializer (Type type, string rootName,
85 : this (type, rootName, rootNamespace, Type.EmptyTypes)
87 // nothing to do here.
90 public DataContractSerializer (Type type,
91 XmlDictionaryString rootName,
92 XmlDictionaryString rootNamespace)
93 : this (type, rootName, rootNamespace, Type.EmptyTypes)
95 // nothing to do here.
98 public DataContractSerializer (Type type, string rootName,
99 string rootNamespace, IEnumerable<Type> knownTypes)
102 throw new ArgumentNullException ("type");
103 if (rootName == null)
104 throw new ArgumentNullException ("rootName");
105 if (rootNamespace == null)
106 throw new ArgumentNullException ("rootNamespace");
108 PopulateTypes (knownTypes);
109 FillDictionaryString (rootName, rootNamespace);
112 public DataContractSerializer (Type type,
113 XmlDictionaryString rootName,
114 XmlDictionaryString rootNamespace,
115 IEnumerable<Type> knownTypes)
118 throw new ArgumentNullException ("type");
119 if (rootName == null)
120 throw new ArgumentNullException ("rootName");
121 if (rootNamespace == null)
122 throw new ArgumentNullException ("rootNamespace");
124 PopulateTypes (knownTypes);
125 root_name = rootName;
126 root_ns = rootNamespace;
129 public DataContractSerializer (Type type,
130 IEnumerable<Type> knownTypes,
131 int maxObjectsInGraph,
132 bool ignoreExtensionDataObject,
133 bool preserveObjectReferences,
134 IDataContractSurrogate dataContractSurrogate)
135 : this (type, knownTypes)
137 Initialize (maxObjectsInGraph,
138 ignoreExtensionDataObject,
139 preserveObjectReferences,
140 dataContractSurrogate);
143 public DataContractSerializer (Type type,
145 string rootNamespace,
146 IEnumerable<Type> knownTypes,
147 int maxObjectsInGraph,
148 bool ignoreExtensionDataObject,
149 bool preserveObjectReferences,
150 IDataContractSurrogate dataContractSurrogate)
151 : this (type, rootName, rootNamespace, knownTypes)
153 Initialize (maxObjectsInGraph,
154 ignoreExtensionDataObject,
155 preserveObjectReferences,
156 dataContractSurrogate);
159 public DataContractSerializer (Type type,
160 XmlDictionaryString rootName,
161 XmlDictionaryString rootNamespace,
162 IEnumerable<Type> knownTypes,
163 int maxObjectsInGraph,
164 bool ignoreExtensionDataObject,
165 bool preserveObjectReferences,
166 IDataContractSurrogate dataContractSurrogate)
167 : this (type, rootName, rootNamespace, knownTypes)
169 Initialize (maxObjectsInGraph,
170 ignoreExtensionDataObject,
171 preserveObjectReferences,
172 dataContractSurrogate);
176 public DataContractSerializer (Type type,
177 IEnumerable<Type> knownTypes,
178 int maxObjectsInGraph,
179 bool ignoreExtensionDataObject,
180 bool preserveObjectReferences,
181 IDataContractSurrogate dataContractSurrogate,
182 DataContractResolver dataContractResolver)
183 : this (type, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
185 DataContractResolver = dataContractResolver;
188 public DataContractSerializer (Type type,
190 string rootNamespace,
191 IEnumerable<Type> knownTypes,
192 int maxObjectsInGraph,
193 bool ignoreExtensionDataObject,
194 bool preserveObjectReferences,
195 IDataContractSurrogate dataContractSurrogate,
196 DataContractResolver dataContractResolver)
197 : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
199 DataContractResolver = dataContractResolver;
202 public DataContractSerializer (Type type,
203 XmlDictionaryString rootName,
204 XmlDictionaryString rootNamespace,
205 IEnumerable<Type> knownTypes,
206 int maxObjectsInGraph,
207 bool ignoreExtensionDataObject,
208 bool preserveObjectReferences,
209 IDataContractSurrogate dataContractSurrogate,
210 DataContractResolver dataContractResolver)
211 : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
213 DataContractResolver = dataContractResolver;
218 public DataContractSerializer (Type type, DataContractSerializerSettings settings)
219 : this (type, settings.RootName, settings.RootNamespace, settings.KnownTypes,
220 settings.MaxItemsInObjectGraph, settings.IgnoreExtensionDataObject,
221 settings.PreserveObjectReferences, settings.DataContractSurrogate,
222 settings.DataContractResolver)
227 void PopulateTypes (IEnumerable<Type> knownTypes)
229 if (known_types == null)
230 known_types = new KnownTypeCollection ();
232 if (specified_known_types == null)
233 specified_known_types = new List<Type> ();
235 if (knownTypes != null) {
236 foreach (Type t in knownTypes) {
238 specified_known_types.Add (t);
242 RegisterTypeAsKnown (type);
245 void RegisterTypeAsKnown (Type type)
247 if (known_types.Contains (type))
250 Type elementType = type;
251 if (type.HasElementType)
252 elementType = type.GetElementType ();
254 known_types.Add (elementType);
256 /* Get all KnownTypeAttribute-s, including inherited ones */
257 object [] attrs = elementType.GetCustomAttributes (typeof (KnownTypeAttribute), true);
258 for (int i = 0; i < attrs.Length; i ++) {
259 KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
260 foreach (var t in kt.GetTypes (elementType))
261 RegisterTypeAsKnown (t);
265 void FillDictionaryString (string name, string ns)
267 XmlDictionary d = new XmlDictionary ();
268 root_name = d.Add (name);
269 root_ns = d.Add (ns);
274 int maxObjectsInGraph,
275 bool ignoreExtensionDataObject,
276 bool preserveObjectReferences,
277 IDataContractSurrogate dataContractSurrogate)
279 if (maxObjectsInGraph < 0)
280 throw new ArgumentOutOfRangeException ("maxObjectsInGraph must not be negative.");
281 max_items = maxObjectsInGraph;
282 ignore_ext = ignoreExtensionDataObject;
283 preserve_refs = preserveObjectReferences;
284 surrogate = dataContractSurrogate;
292 DataContractResolver DataContractResolver {
293 get { return resolver; }
296 default_resolver = default_resolver ?? new DefaultDataContractResolver (this);
300 public bool IgnoreExtensionDataObject {
301 get { return ignore_ext; }
304 public ReadOnlyCollection<Type> KnownTypes {
306 if (returned_known_types == null)
307 returned_known_types = new ReadOnlyCollection<Type> (specified_known_types);
308 return returned_known_types;
312 internal KnownTypeCollection InternalKnownTypes {
313 get { return known_types; }
316 public IDataContractSurrogate DataContractSurrogate {
317 get { return surrogate; }
320 public int MaxItemsInObjectGraph {
321 get { return max_items; }
324 public bool PreserveObjectReferences {
325 get { return preserve_refs; }
328 public override bool IsStartObject (XmlDictionaryReader reader)
331 throw new ArgumentNullException ("reader");
332 reader.MoveToContent ();
333 return reader.IsStartElement (root_name, root_ns);
337 public override bool IsStartObject (XmlReader reader)
339 return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader));
343 public override object ReadObject (XmlReader reader)
345 return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader));
348 public override object ReadObject (XmlReader reader, bool verifyObjectName)
350 return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader), verifyObjectName);
353 public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
355 int startTypeCount = known_types.Count;
356 known_types.Add (type);
358 bool isEmpty = reader.IsEmptyElement;
360 object ret = XmlFormatterDeserializer.Deserialize (reader, type,
361 known_types, surrogate, DataContractResolver, default_resolver, root_name.Value, root_ns.Value, verifyObjectName);
363 // remove temporarily-added known types for
364 // rootType and object graph type.
365 while (known_types.Count > startTypeCount)
366 known_types.RemoveAt (startTypeCount);
372 public object ReadObject (XmlDictionaryReader reader, bool verifyObjectName, DataContractResolver resolver)
374 var bak = DataContractResolver;
376 DataContractResolver = resolver;
377 return ReadObject (reader, verifyObjectName);
379 DataContractResolver = bak;
385 public override void WriteObject (XmlWriter writer, object graph)
387 XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
388 WriteObject (w, graph);
392 public void WriteObject (XmlDictionaryWriter writer, object graph, DataContractResolver resolver)
394 var bak = DataContractResolver;
396 DataContractResolver = resolver;
397 WriteObject (writer, graph);
399 DataContractResolver = bak;
404 [MonoTODO ("use DataContractSurrogate")]
406 when writeContentOnly is true, then the input XmlWriter
407 must be at element state. This is to write possible
410 rootType determines the top-level element QName (thus
411 it is ignored when writeContentOnly is true).
413 preserveObjectReferences indicates that whether the
414 output should contain ms:Id or not.
415 (http://schemas.microsoft.com/2003/10/Serialization/)
417 public override void WriteObjectContent (XmlDictionaryWriter writer, object graph)
422 int startTypeCount = known_types.Count;
424 XmlFormatterSerializer.Serialize (writer, graph,
426 ignore_ext, max_items, root_ns.Value, preserve_refs, DataContractResolver, default_resolver);
428 // remove temporarily-added known types for
429 // rootType and object graph type.
430 while (known_types.Count > startTypeCount)
431 known_types.RemoveAt (startTypeCount);
434 public override void WriteObjectContent (XmlWriter writer, object graph)
436 XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
437 WriteObjectContent (w, graph);
441 public override void WriteStartObject (
442 XmlWriter writer, object graph)
444 WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph);
447 public override void WriteStartObject (
448 XmlDictionaryWriter writer, object graph)
450 Type rootType = type;
452 if (root_name.Value == "")
453 throw new InvalidDataContractException ("Type '" + type.ToString () +
454 "' cannot have a DataContract attribute Name set to null or empty string.");
459 writer.WriteStartElement (root_name.Value, root_ns.Value);
461 writer.WriteStartElement (root_name, root_ns);
462 writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
466 QName rootQName = null;
467 XmlDictionaryString name, ns;
468 var graphType = graph.GetType ();
469 if (DataContractResolver != null && DataContractResolver.TryResolveType (graphType, type, default_resolver, out name, out ns))
470 rootQName = new QName (name.Value, ns.Value);
472 // It is error unless 1) TypeResolver resolved the type name, 2) the object is the exact type, 3) the object is known or 4) the type is primitive.
474 QName collectionQName;
475 if (KnownTypeCollection.IsInterchangeableCollectionType (type, graphType, out collectionQName)) {
477 rootQName = collectionQName;
478 } else if (graphType != type && rootQName == null && IsUnknownType (type, graphType))
479 throw new SerializationException (String.Format ("Type '{0}' is unexpected. The type should either be registered as a known type, or DataContractResolver should be used.", graphType));
481 QName instName = rootQName;
482 rootQName = rootQName ?? known_types.GetQName (rootType);
483 QName graph_qname = known_types.GetQName (graphType);
485 known_types.Add (graphType);
488 writer.WriteStartElement (root_name.Value, root_ns.Value);
490 writer.WriteStartElement (root_name, root_ns);
492 if (rootQName != graph_qname || rootQName.Namespace != KnownTypeCollection.MSSimpleNamespace && !rootType.IsEnum)
493 //FIXME: Hack, when should the "i:type" be written?
494 //Not used in case of enums
495 writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
497 if (root_ns.Value != rootQName.Namespace)
498 if (rootQName.Namespace != KnownTypeCollection.MSSimpleNamespace)
499 writer.WriteXmlnsAttribute (null, rootQName.Namespace);
501 if (rootQName == graph_qname)
504 /* Different names */
505 known_types.Add (rootType);
507 instName = instName ?? KnownTypeCollection.GetPredefinedTypeName (graphType);
508 if (instName == QName.Empty)
509 /* Not a primitive type */
510 instName = graph_qname;
512 /* FIXME: Hack, .. see test WriteObject7 () */
513 instName = new QName (instName.Name, XmlSchema.Namespace);
515 /* // disabled as it now generates extraneous i:type output.
516 // output xsi:type as rootType is not equivalent to the graph's type.
517 writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
518 writer.WriteQualifiedName (instName.Name, instName.Namespace);
519 writer.WriteEndAttribute ();
523 bool IsUnknownType (Type contractType, Type type)
526 if (KnownTypeCollection.GetAttribute<CollectionDataContractAttribute> (contractType) != null ||
527 KnownTypeCollection.GetAttribute<DataContractAttribute> (contractType) != null)
530 return IsUnknownType (type);
533 bool IsUnknownType (Type type)
535 if (known_types.Contains (type) ||
536 KnownTypeCollection.GetPrimitiveTypeName (type) != QName.Empty)
539 return IsUnknownType (type.GetElementType ());
543 public override void WriteEndObject (XmlDictionaryWriter writer)
545 writer.WriteEndElement ();
549 public override void WriteEndObject (XmlWriter writer)
551 WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));
556 public bool SerializeReadOnlyTypes {
557 get { throw new NotImplementedException (); }