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;
38 using System.Xml.Serialization;
40 using QName = System.Xml.XmlQualifiedName;
42 namespace System.Runtime.Serialization
44 public sealed class DataContractSerializer : XmlObjectSerializer
46 const string xmlns = "http://www.w3.org/2000/xmlns/";
49 bool ignore_ext, preserve_refs;
51 // This is only for compatible mode.
52 StreamingContext context;
53 ReadOnlyCollection<Type> returned_known_types;
54 KnownTypeCollection known_types;
55 List<Type> specified_known_types;
56 IDataContractSurrogate surrogate;
57 DataContractResolver resolver, default_resolver;
59 int max_items = 0x10000; // FIXME: could be from config.
62 XmlDictionaryString root_name, root_ns;
64 public DataContractSerializer (Type type)
65 : this (type, Type.EmptyTypes)
67 // nothing to do here.
70 public DataContractSerializer (Type type,
71 IEnumerable<Type> knownTypes)
74 throw new ArgumentNullException ("type");
76 PopulateTypes (knownTypes);
77 known_types.Add (type);
78 QName qname = known_types.GetQName (type);
80 FillDictionaryString (qname.Name, qname.Namespace);
84 public DataContractSerializer (Type type, string rootName,
86 : this (type, rootName, rootNamespace, Type.EmptyTypes)
88 // nothing to do here.
91 public DataContractSerializer (Type type,
92 XmlDictionaryString rootName,
93 XmlDictionaryString rootNamespace)
94 : this (type, rootName, rootNamespace, Type.EmptyTypes)
96 // nothing to do here.
99 public DataContractSerializer (Type type, string rootName,
100 string rootNamespace, IEnumerable<Type> knownTypes)
103 throw new ArgumentNullException ("type");
104 if (rootName == null)
105 throw new ArgumentNullException ("rootName");
106 if (rootNamespace == null)
107 throw new ArgumentNullException ("rootNamespace");
109 PopulateTypes (knownTypes);
110 FillDictionaryString (rootName, rootNamespace);
113 public DataContractSerializer (Type type,
114 XmlDictionaryString rootName,
115 XmlDictionaryString rootNamespace,
116 IEnumerable<Type> knownTypes)
119 throw new ArgumentNullException ("type");
120 if (rootName == null)
121 throw new ArgumentNullException ("rootName");
122 if (rootNamespace == null)
123 throw new ArgumentNullException ("rootNamespace");
125 PopulateTypes (knownTypes);
126 root_name = rootName;
127 root_ns = rootNamespace;
130 public DataContractSerializer (Type type,
131 IEnumerable<Type> knownTypes,
132 int maxObjectsInGraph,
133 bool ignoreExtensionDataObject,
134 bool preserveObjectReferences,
135 IDataContractSurrogate dataContractSurrogate)
136 : this (type, knownTypes)
138 Initialize (maxObjectsInGraph,
139 ignoreExtensionDataObject,
140 preserveObjectReferences,
141 dataContractSurrogate);
144 public DataContractSerializer (Type type,
146 string rootNamespace,
147 IEnumerable<Type> knownTypes,
148 int maxObjectsInGraph,
149 bool ignoreExtensionDataObject,
150 bool preserveObjectReferences,
151 IDataContractSurrogate dataContractSurrogate)
152 : this (type, rootName, rootNamespace, knownTypes)
154 Initialize (maxObjectsInGraph,
155 ignoreExtensionDataObject,
156 preserveObjectReferences,
157 dataContractSurrogate);
160 public DataContractSerializer (Type type,
161 XmlDictionaryString rootName,
162 XmlDictionaryString rootNamespace,
163 IEnumerable<Type> knownTypes,
164 int maxObjectsInGraph,
165 bool ignoreExtensionDataObject,
166 bool preserveObjectReferences,
167 IDataContractSurrogate dataContractSurrogate)
168 : this (type, rootName, rootNamespace, knownTypes)
170 Initialize (maxObjectsInGraph,
171 ignoreExtensionDataObject,
172 preserveObjectReferences,
173 dataContractSurrogate);
177 public DataContractSerializer (Type type,
178 IEnumerable<Type> knownTypes,
179 int maxObjectsInGraph,
180 bool ignoreExtensionDataObject,
181 bool preserveObjectReferences,
182 IDataContractSurrogate dataContractSurrogate,
183 DataContractResolver dataContractResolver)
184 : this (type, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
186 DataContractResolver = dataContractResolver;
189 public DataContractSerializer (Type type,
191 string rootNamespace,
192 IEnumerable<Type> knownTypes,
193 int maxObjectsInGraph,
194 bool ignoreExtensionDataObject,
195 bool preserveObjectReferences,
196 IDataContractSurrogate dataContractSurrogate,
197 DataContractResolver dataContractResolver)
198 : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
200 DataContractResolver = dataContractResolver;
203 public DataContractSerializer (Type type,
204 XmlDictionaryString rootName,
205 XmlDictionaryString rootNamespace,
206 IEnumerable<Type> knownTypes,
207 int maxObjectsInGraph,
208 bool ignoreExtensionDataObject,
209 bool preserveObjectReferences,
210 IDataContractSurrogate dataContractSurrogate,
211 DataContractResolver dataContractResolver)
212 : this (type, rootName, rootNamespace, knownTypes, maxObjectsInGraph, ignoreExtensionDataObject, preserveObjectReferences, dataContractSurrogate)
214 DataContractResolver = dataContractResolver;
219 public DataContractSerializer (Type type, DataContractSerializerSettings settings)
220 : this (type, settings.RootName, settings.RootNamespace, settings.KnownTypes,
221 settings.MaxItemsInObjectGraph, settings.IgnoreExtensionDataObject,
222 settings.PreserveObjectReferences, settings.DataContractSurrogate,
223 settings.DataContractResolver)
228 void PopulateTypes (IEnumerable<Type> knownTypes)
230 if (known_types == null)
231 known_types = new KnownTypeCollection ();
233 if (specified_known_types == null)
234 specified_known_types = new List<Type> ();
236 if (knownTypes != null) {
237 foreach (Type t in knownTypes) {
239 specified_known_types.Add (t);
243 RegisterTypeAsKnown (type);
246 void RegisterTypeAsKnown (Type type)
248 if (known_types.Contains (type))
251 Type elementType = type;
252 if (type.HasElementType) {
253 known_types.Add (type);
254 elementType = type.GetElementType ();
257 known_types.Add (elementType);
259 /* Get all KnownTypeAttribute-s, including inherited ones */
260 object [] attrs = elementType.GetCustomAttributes (typeof (KnownTypeAttribute), true);
261 for (int i = 0; i < attrs.Length; i ++) {
262 KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
263 foreach (var t in kt.GetTypes (elementType))
264 RegisterTypeAsKnown (t);
268 void FillDictionaryString (string name, string ns)
270 XmlDictionary d = new XmlDictionary ();
271 root_name = d.Add (name);
272 root_ns = d.Add (ns);
277 int maxObjectsInGraph,
278 bool ignoreExtensionDataObject,
279 bool preserveObjectReferences,
280 IDataContractSurrogate dataContractSurrogate)
282 if (maxObjectsInGraph < 0)
283 throw new ArgumentOutOfRangeException ("maxObjectsInGraph must not be negative.");
284 max_items = maxObjectsInGraph;
285 ignore_ext = ignoreExtensionDataObject;
286 preserve_refs = preserveObjectReferences;
287 surrogate = dataContractSurrogate;
295 DataContractResolver DataContractResolver {
296 get { return resolver; }
299 default_resolver = default_resolver ?? new DefaultDataContractResolver (this);
303 public bool IgnoreExtensionDataObject {
304 get { return ignore_ext; }
307 public ReadOnlyCollection<Type> KnownTypes {
309 if (returned_known_types == null)
310 returned_known_types = new ReadOnlyCollection<Type> (specified_known_types);
311 return returned_known_types;
315 internal KnownTypeCollection InternalKnownTypes {
316 get { return known_types; }
319 public IDataContractSurrogate DataContractSurrogate {
320 get { return surrogate; }
323 public int MaxItemsInObjectGraph {
324 get { return max_items; }
327 public bool PreserveObjectReferences {
328 get { return preserve_refs; }
331 public override bool IsStartObject (XmlDictionaryReader reader)
334 throw new ArgumentNullException ("reader");
335 reader.MoveToContent ();
336 return reader.IsStartElement (root_name, root_ns);
340 public override bool IsStartObject (XmlReader reader)
342 return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader));
346 public override object ReadObject (XmlReader reader)
348 return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader));
351 public override object ReadObject (XmlReader reader, bool verifyObjectName)
353 return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader), verifyObjectName);
356 public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
358 int startTypeCount = known_types.Count;
359 known_types.Add (type);
361 bool isEmpty = reader.IsEmptyElement;
363 object ret = XmlFormatterDeserializer.Deserialize (reader, type,
364 known_types, surrogate, DataContractResolver, default_resolver, root_name.Value, root_ns.Value, verifyObjectName);
366 // remove temporarily-added known types for
367 // rootType and object graph type.
368 while (known_types.Count > startTypeCount)
369 known_types.RemoveAt (startTypeCount);
375 public object ReadObject (XmlDictionaryReader reader, bool verifyObjectName, DataContractResolver resolver)
377 var bak = DataContractResolver;
379 DataContractResolver = resolver;
380 return ReadObject (reader, verifyObjectName);
382 DataContractResolver = bak;
388 public override void WriteObject (XmlWriter writer, object graph)
390 XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
391 WriteObject (w, graph);
395 public void WriteObject (XmlDictionaryWriter writer, object graph, DataContractResolver resolver)
397 var bak = DataContractResolver;
399 DataContractResolver = resolver;
400 WriteObject (writer, graph);
402 DataContractResolver = bak;
407 [MonoTODO ("use DataContractSurrogate")]
409 when writeContentOnly is true, then the input XmlWriter
410 must be at element state. This is to write possible
413 rootType determines the top-level element QName (thus
414 it is ignored when writeContentOnly is true).
416 preserveObjectReferences indicates that whether the
417 output should contain ms:Id or not.
418 (http://schemas.microsoft.com/2003/10/Serialization/)
420 public override void WriteObjectContent (XmlDictionaryWriter writer, object graph)
425 int startTypeCount = known_types.Count;
427 XmlFormatterSerializer.Serialize (writer, graph,
429 ignore_ext, max_items, root_ns.Value, preserve_refs, DataContractResolver, default_resolver);
431 // remove temporarily-added known types for
432 // rootType and object graph type.
433 while (known_types.Count > startTypeCount)
434 known_types.RemoveAt (startTypeCount);
437 public override void WriteObjectContent (XmlWriter writer, object graph)
439 XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
440 WriteObjectContent (w, graph);
444 public override void WriteStartObject (
445 XmlWriter writer, object graph)
447 WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph);
450 public override void WriteStartObject (
451 XmlDictionaryWriter writer, object graph)
453 Type rootType = type;
458 if (root_name.Value == "")
459 throw new InvalidDataContractException ("Type '" + type.ToString () +
460 "' cannot have a DataContract attribute Name set to null or empty string.");
464 writer.WriteStartElement (root_name.Value, root_ns.Value);
466 writer.WriteStartElement (root_name, root_ns);
467 writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
471 QName rootQName = null;
472 XmlDictionaryString name, ns;
473 var graphType = graph.GetType ();
474 if (DataContractResolver != null && DataContractResolver.TryResolveType (graphType, type, default_resolver, out name, out ns))
475 rootQName = new QName (name.Value, ns.Value);
477 // 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.
479 QName collectionQName;
480 if (KnownTypeCollection.IsInterchangeableCollectionType (type, graphType, out collectionQName)) {
482 rootQName = collectionQName;
483 } else if (graphType != type && rootQName == null && IsUnknownType (type, graphType))
484 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));
486 QName instName = rootQName;
487 rootQName = rootQName ?? known_types.GetQName (rootType);
488 QName graph_qname = known_types.GetQName (graphType);
490 known_types.Add (graphType);
493 writer.WriteStartElement (root_name.Value, root_ns.Value);
495 writer.WriteStartElement (root_name, root_ns);
497 if (rootQName != graph_qname || rootQName.Namespace != KnownTypeCollection.MSSimpleNamespace && !rootType.IsEnum)
498 //FIXME: Hack, when should the "i:type" be written?
499 //Not used in case of enums
500 writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
502 if (root_ns.Value != rootQName.Namespace)
503 if (rootQName.Namespace != KnownTypeCollection.MSSimpleNamespace)
504 writer.WriteXmlnsAttribute (null, rootQName.Namespace);
506 if (rootQName == graph_qname)
509 /* Different names */
510 known_types.Add (rootType);
512 instName = instName ?? KnownTypeCollection.GetPredefinedTypeName (graphType);
513 if (instName == QName.Empty)
514 /* Not a primitive type */
515 instName = graph_qname;
517 /* FIXME: Hack, .. see test WriteObject7 () */
518 instName = new QName (instName.Name, XmlSchema.Namespace);
520 /* // disabled as it now generates extraneous i:type output.
521 // output xsi:type as rootType is not equivalent to the graph's type.
522 writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
523 writer.WriteQualifiedName (instName.Name, instName.Namespace);
524 writer.WriteEndAttribute ();
528 bool IsUnknownType (Type contractType, Type type)
531 if (KnownTypeCollection.GetAttribute<CollectionDataContractAttribute> (contractType) != null ||
532 KnownTypeCollection.GetAttribute<DataContractAttribute> (contractType) != null)
535 return IsUnknownType (type);
538 bool IsUnknownType (Type type)
540 if (known_types.Contains (type) ||
541 KnownTypeCollection.GetPrimitiveTypeName (type) != QName.Empty)
544 return IsUnknownType (type.GetElementType ());
548 public override void WriteEndObject (XmlDictionaryWriter writer)
553 writer.WriteEndElement ();
557 public override void WriteEndObject (XmlWriter writer)
559 WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));
564 public bool SerializeReadOnlyTypes {
565 get { throw new NotImplementedException (); }
569 private bool IsAny ()
571 var xpa = type.GetCustomAttribute<XmlSchemaProviderAttribute> (true);