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; }
241 public override bool IsStartObject (XmlDictionaryReader reader
)
243 throw new NotImplementedException ();
247 public override bool IsStartObject (XmlReader reader
)
249 return IsStartObject (XmlDictionaryReader
.CreateDictionaryReader (reader
));
253 public override object ReadObject (XmlReader reader
)
255 return ReadObject (XmlDictionaryReader
.CreateDictionaryReader (reader
));
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
);
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?
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
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
)
314 int startTypeCount
= known_types
.Count
;
316 XmlFormatterSerializer
.Serialize (writer
, graph
,
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
);
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.");
345 writer
.WriteStartElement (root_name
.Value
, root_ns
.Value
);
347 writer
.WriteStartElement (root_name
, root_ns
);
348 writer
.WriteAttributeString ("i", "nil", XmlSchema
.InstanceNamespace
, "true");
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 ());
359 writer
.WriteStartElement (root_name
.Value
, root_ns
.Value
);
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
&&
369 //FIXME: Hack, when should the "i:type" be written?
370 //Not used in case of enums
371 writer
.WriteXmlnsAttribute ("i", XmlSchema
.InstanceNamespace
);
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
;
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 ();
399 public override void WriteEndObject (XmlWriter writer
)
401 WriteEndObject (XmlDictionaryWriter
.CreateDictionaryWriter (writer
));