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.
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
)
71 throw new ArgumentNullException ("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
,
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
)
101 throw new ArgumentNullException ("type");
102 if (rootName
== null)
103 throw new ArgumentNullException ("rootName");
104 if (rootNamespace
== null)
105 throw new ArgumentNullException ("rootNamespace");
107 PopulateTypes (knownTypes
);
108 FillDictionaryString (rootName
, rootNamespace
);
111 public DataContractSerializer (Type type
,
112 XmlDictionaryString rootName
,
113 XmlDictionaryString rootNamespace
,
114 IEnumerable
<Type
> knownTypes
)
117 throw new ArgumentNullException ("type");
118 if (rootName
== null)
119 throw new ArgumentNullException ("rootName");
120 if (rootNamespace
== null)
121 throw new ArgumentNullException ("rootNamespace");
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
,
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
);
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 public override bool IsStartObject (XmlDictionaryReader reader
)
243 throw new ArgumentNullException ("reader");
244 reader
.MoveToContent ();
245 return reader
.IsStartElement (root_name
, root_ns
);
249 public override bool IsStartObject (XmlReader reader
)
251 return IsStartObject (XmlDictionaryReader
.CreateDictionaryReader (reader
));
255 public override object ReadObject (XmlReader reader
)
257 return ReadObject (XmlDictionaryReader
.CreateDictionaryReader (reader
));
260 public override object ReadObject (XmlReader reader
, bool verifyObjectName
)
262 return ReadObject (XmlDictionaryReader
.CreateDictionaryReader (reader
), verifyObjectName
);
266 public override object ReadObject (XmlDictionaryReader reader
, bool verifyObjectName
)
268 int startTypeCount
= known_types
.Count
;
269 known_types
.Add (type
);
271 bool isEmpty
= reader
.IsEmptyElement
;
273 object ret
= XmlFormatterDeserializer
.Deserialize (reader
, type
,
274 known_types
, surrogate
, root_name
.Value
, root_ns
.Value
, verifyObjectName
);
276 // remove temporarily-added known types for
277 // rootType and object graph type.
278 while (known_types
.Count
> startTypeCount
)
279 known_types
.RemoveAt (startTypeCount
);
284 private void ReadRootStartElement (XmlReader reader
, Type type
)
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?
297 public override void WriteObject (XmlWriter writer
, object graph
)
299 XmlDictionaryWriter w
= XmlDictionaryWriter
.CreateDictionaryWriter (writer
);
300 WriteObject (w
, graph
);
303 [MonoTODO ("support arrays; support Serializable; support SharedType; use DataContractSurrogate")]
305 when writeContentOnly is true, then the input XmlWriter
306 must be at element state. This is to write possible
309 rootType determines the top-level element QName (thus
310 it is ignored when writeContentOnly is true).
312 preserveObjectReferences indicates that whether the
313 output should contain ms:Id or not.
314 (http://schemas.microsoft.com/2003/10/Serialization/)
316 public override void WriteObjectContent (XmlDictionaryWriter writer
, object graph
)
321 int startTypeCount
= known_types
.Count
;
323 XmlFormatterSerializer
.Serialize (writer
, graph
,
325 ignore_ext
, max_items
, root_ns
.Value
);
327 // remove temporarily-added known types for
328 // rootType and object graph type.
329 while (known_types
.Count
> startTypeCount
)
330 known_types
.RemoveAt (startTypeCount
);
333 public override void WriteObjectContent (XmlWriter writer
, object graph
)
335 XmlDictionaryWriter w
= XmlDictionaryWriter
.CreateDictionaryWriter (writer
);
336 WriteObjectContent (w
, graph
);
340 public override void WriteStartObject (
341 XmlWriter writer
, object graph
)
343 WriteStartObject (XmlDictionaryWriter
.CreateDictionaryWriter (writer
), graph
);
346 public override void WriteStartObject (
347 XmlDictionaryWriter writer
, object graph
)
349 Type rootType
= type
;
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.");
358 writer
.WriteStartElement (root_name
.Value
, root_ns
.Value
);
360 writer
.WriteStartElement (root_name
, root_ns
);
361 writer
.WriteAttributeString ("i", "nil", XmlSchema
.InstanceNamespace
, "true");
365 QName instName
= null;
366 QName root_qname
= known_types
.GetQName (rootType
);
367 QName graph_qname
= known_types
.GetQName (graph
.GetType ());
369 known_types
.Add (graph
.GetType ());
372 writer
.WriteStartElement (root_name
.Value
, root_ns
.Value
);
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
);
379 if (root_qname
== graph_qname
) {
380 if (root_qname
.Namespace
!= KnownTypeCollection
.MSSimpleNamespace
&&
382 //FIXME: Hack, when should the "i:type" be written?
383 //Not used in case of enums
384 writer
.WriteXmlnsAttribute ("i", XmlSchema
.InstanceNamespace
);
389 /* Different names */
390 known_types
.Add (rootType
);
392 instName
= KnownTypeCollection
.GetPredefinedTypeName (graph
.GetType ());
393 if (instName
== QName
.Empty
)
394 /* Not a primitive type */
395 instName
= graph_qname
;
397 /* FIXME: Hack, .. see test WriteObject7 () */
398 instName
= new QName (instName
.Name
, XmlSchema
.Namespace
);
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 ();
406 public override void WriteEndObject (XmlDictionaryWriter writer
)
408 writer
.WriteEndElement ();
412 public override void WriteEndObject (XmlWriter writer
)
414 WriteEndObject (XmlDictionaryWriter
.CreateDictionaryWriter (writer
));