DataContractSerializer.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. //
  2. // DataContractSerializer.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // Copyright (C) 2005-2007 Novell, Inc. http://www.novell.com
  8. //
  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:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  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.
  27. //
  28. #if NET_2_0
  29. using System;
  30. using System.Collections;
  31. using System.Collections.Generic;
  32. using System.Collections.ObjectModel;
  33. using System.IO;
  34. using System.Reflection;
  35. using System.Runtime.Serialization.Formatters.Binary;
  36. using System.Xml;
  37. using System.Xml.Schema;
  38. using QName = System.Xml.XmlQualifiedName;
  39. namespace System.Runtime.Serialization
  40. {
  41. public sealed class DataContractSerializer : XmlObjectSerializer
  42. {
  43. const string xmlns = "http://www.w3.org/2000/xmlns/";
  44. Type type;
  45. bool ignore_ext, preserve_refs;
  46. // This is only for compatible mode.
  47. StreamingContext context;
  48. ReadOnlyCollection<Type> known_runtime_types;
  49. KnownTypeCollection known_types;
  50. IDataContractSurrogate surrogate;
  51. int max_items = 0x10000; // FIXME: could be from config.
  52. XmlDictionaryString root_name, root_ns;
  53. public DataContractSerializer (Type type)
  54. : this (type, Type.EmptyTypes)
  55. {
  56. // nothing to do here.
  57. }
  58. public DataContractSerializer (Type type,
  59. IEnumerable<Type> knownTypes)
  60. {
  61. if (type == null)
  62. throw new ArgumentNullException ("type");
  63. this.type = type;
  64. known_types = new KnownTypeCollection ();
  65. PopulateTypes (knownTypes);
  66. known_types.TryRegister (type);
  67. QName qname = known_types.GetQName (type);
  68. FillDictionaryString (qname.Name, qname.Namespace);
  69. }
  70. public DataContractSerializer (Type type, string rootName,
  71. string rootNamespace)
  72. : this (type, rootName, rootNamespace, Type.EmptyTypes)
  73. {
  74. // nothing to do here.
  75. }
  76. public DataContractSerializer (Type type,
  77. XmlDictionaryString rootName,
  78. XmlDictionaryString rootNamespace)
  79. : this (type, rootName, rootNamespace, Type.EmptyTypes)
  80. {
  81. // nothing to do here.
  82. }
  83. public DataContractSerializer (Type type, string rootName,
  84. string rootNamespace, IEnumerable<Type> knownTypes)
  85. {
  86. if (type == null)
  87. throw new ArgumentNullException ("type");
  88. if (rootName == null)
  89. throw new ArgumentNullException ("rootName");
  90. if (rootNamespace == null)
  91. throw new ArgumentNullException ("rootNamespace");
  92. this.type = type;
  93. PopulateTypes (knownTypes);
  94. FillDictionaryString (rootName, rootNamespace);
  95. }
  96. public DataContractSerializer (Type type,
  97. XmlDictionaryString rootName,
  98. XmlDictionaryString rootNamespace,
  99. IEnumerable<Type> knownTypes)
  100. {
  101. if (type == null)
  102. throw new ArgumentNullException ("type");
  103. if (rootName == null)
  104. throw new ArgumentNullException ("rootName");
  105. if (rootNamespace == null)
  106. throw new ArgumentNullException ("rootNamespace");
  107. this.type = type;
  108. PopulateTypes (knownTypes);
  109. root_name = rootName;
  110. root_ns = rootNamespace;
  111. }
  112. public DataContractSerializer (Type type,
  113. IEnumerable<Type> knownTypes,
  114. int maxObjectsInGraph,
  115. bool ignoreExtensionDataObject,
  116. bool preserveObjectReferences,
  117. IDataContractSurrogate dataContractSurrogate)
  118. : this (type, knownTypes)
  119. {
  120. Initialize (maxObjectsInGraph,
  121. ignoreExtensionDataObject,
  122. preserveObjectReferences,
  123. dataContractSurrogate);
  124. }
  125. public DataContractSerializer (Type type,
  126. string rootName,
  127. string rootNamespace,
  128. IEnumerable<Type> knownTypes,
  129. int maxObjectsInGraph,
  130. bool ignoreExtensionDataObject,
  131. bool preserveObjectReferences,
  132. IDataContractSurrogate dataContractSurrogate)
  133. : this (type, rootName, rootNamespace, knownTypes)
  134. {
  135. Initialize (maxObjectsInGraph,
  136. ignoreExtensionDataObject,
  137. preserveObjectReferences,
  138. dataContractSurrogate);
  139. }
  140. public DataContractSerializer (Type type,
  141. XmlDictionaryString rootName,
  142. XmlDictionaryString rootNamespace,
  143. IEnumerable<Type> knownTypes,
  144. int maxObjectsInGraph,
  145. bool ignoreExtensionDataObject,
  146. bool preserveObjectReferences,
  147. IDataContractSurrogate dataContractSurrogate)
  148. : this (type, rootName, rootNamespace, knownTypes)
  149. {
  150. Initialize (maxObjectsInGraph,
  151. ignoreExtensionDataObject,
  152. preserveObjectReferences,
  153. dataContractSurrogate);
  154. }
  155. void PopulateTypes (IEnumerable<Type> knownTypes)
  156. {
  157. if (known_types == null)
  158. known_types= new KnownTypeCollection ();
  159. if (knownTypes != null) {
  160. foreach (Type t in knownTypes)
  161. known_types.TryRegister (t);
  162. }
  163. Type elementType = type;
  164. if (type.HasElementType)
  165. elementType = type.GetElementType ();
  166. /* Get all KnownTypeAttribute-s, including inherited ones */
  167. object [] attrs = elementType.GetCustomAttributes (typeof (KnownTypeAttribute), true);
  168. for (int i = 0; i < attrs.Length; i ++) {
  169. KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
  170. known_types.TryRegister (kt.Type);
  171. }
  172. }
  173. void FillDictionaryString (string name, string ns)
  174. {
  175. XmlDictionary d = new XmlDictionary ();
  176. root_name = d.Add (name);
  177. root_ns = d.Add (ns);
  178. }
  179. void Initialize (
  180. int maxObjectsInGraph,
  181. bool ignoreExtensionDataObject,
  182. bool preserveObjectReferences,
  183. IDataContractSurrogate dataContractSurrogate)
  184. {
  185. if (maxObjectsInGraph < 0)
  186. throw new ArgumentOutOfRangeException ("maxObjectsInGraph must not be negative.");
  187. max_items = maxObjectsInGraph;
  188. ignore_ext = ignoreExtensionDataObject;
  189. preserve_refs = preserveObjectReferences;
  190. surrogate = dataContractSurrogate;
  191. PopulateTypes (Type.EmptyTypes);
  192. }
  193. public bool IgnoreExtensionDataObject {
  194. get { return ignore_ext; }
  195. }
  196. public ReadOnlyCollection<Type> KnownTypes {
  197. get { return known_runtime_types; }
  198. }
  199. public IDataContractSurrogate DataContractSurrogate {
  200. get { return surrogate; }
  201. }
  202. public int MaxItemsInObjectGraph {
  203. get { return max_items; }
  204. }
  205. public bool PreserveObjectReferences {
  206. get { return preserve_refs; }
  207. }
  208. [MonoTODO]
  209. public override bool IsStartObject (XmlDictionaryReader reader)
  210. {
  211. throw new NotImplementedException ();
  212. }
  213. // SP1
  214. public override bool IsStartObject (XmlReader reader)
  215. {
  216. return IsStartObject (XmlDictionaryReader.CreateDictionaryReader (reader));
  217. }
  218. // SP1
  219. public override object ReadObject (XmlReader reader)
  220. {
  221. return ReadObject (XmlDictionaryReader.CreateDictionaryReader (reader));
  222. }
  223. [MonoTODO]
  224. public override object ReadObject (XmlDictionaryReader reader, bool verifyObjectName)
  225. {
  226. int startTypeCount = known_types.Count;
  227. known_types.Add (type);
  228. bool isEmpty = reader.IsEmptyElement;
  229. object ret = XmlFormatterDeserializer.Deserialize (reader, type,
  230. known_types, surrogate, root_name.Value, root_ns.Value, verifyObjectName);
  231. // remove temporarily-added known types for
  232. // rootType and object graph type.
  233. while (known_types.Count > startTypeCount)
  234. known_types.RemoveAt (startTypeCount);
  235. return ret;
  236. }
  237. private void ReadRootStartElement (XmlReader reader, Type type)
  238. {
  239. SerializationMap map =
  240. known_types.FindUserMap (type);
  241. QName name = map != null ? map.XmlName :
  242. KnownTypeCollection.GetPredefinedTypeName (type);
  243. reader.MoveToContent ();
  244. reader.ReadStartElement (name.Name, name.Namespace);
  245. // FIXME: could there be any attributes to handle here?
  246. reader.Read ();
  247. }
  248. // SP1
  249. public override void WriteObject (XmlWriter writer, object graph)
  250. {
  251. XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (writer);
  252. WriteObject (w, graph);
  253. }
  254. [MonoTODO ("support arrays; support Serializable; support SharedType; use DataContractSurrogate")]
  255. /*
  256. when writeContentOnly is true, then the input XmlWriter
  257. must be at element state. This is to write possible
  258. xsi:nil.
  259. rootType determines the top-level element QName (thus
  260. it is ignored when writeContentOnly is true).
  261. preserveObjectReferences indicates that whether the
  262. output should contain ms:Id or not.
  263. (http://schemas.microsoft.com/2003/10/Serialization/)
  264. */
  265. public override void WriteObjectContent (XmlDictionaryWriter writer, object graph)
  266. {
  267. if (graph == null)
  268. return;
  269. int startTypeCount = known_types.Count;
  270. XmlFormatterSerializer.Serialize (writer, graph,
  271. known_types,
  272. ignore_ext, max_items, root_ns.Value);
  273. // remove temporarily-added known types for
  274. // rootType and object graph type.
  275. while (known_types.Count > startTypeCount)
  276. known_types.RemoveAt (startTypeCount);
  277. }
  278. // SP1
  279. public override void WriteStartObject (
  280. XmlWriter writer, object graph)
  281. {
  282. WriteStartObject (XmlDictionaryWriter.CreateDictionaryWriter (writer), graph);
  283. }
  284. public override void WriteStartObject (
  285. XmlDictionaryWriter writer, object graph)
  286. {
  287. Type rootType = type;
  288. if (root_name.Value == "")
  289. throw new InvalidDataContractException ("Type '" + type.ToString () +
  290. "' cannot have a DataContract attribute Name set to null or empty string.");
  291. if (graph == null) {
  292. writer.WriteStartElement (root_name.Value, root_ns.Value);
  293. writer.WriteAttributeString ("i", "nil", XmlSchema.InstanceNamespace, "true");
  294. return;
  295. }
  296. QName instName = null;
  297. QName root_qname = known_types.GetQName (rootType);
  298. QName graph_qname = known_types.GetQName (graph.GetType ());
  299. known_types.Add (graph.GetType ());
  300. writer.WriteStartElement (root_name.Value, root_ns.Value);
  301. if (root_ns.Value != root_qname.Namespace)
  302. if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace)
  303. writer.WriteXmlnsAttribute (null, root_qname.Namespace);
  304. if (root_qname == graph_qname) {
  305. if (root_qname.Namespace != KnownTypeCollection.MSSimpleNamespace &&
  306. !rootType.IsEnum)
  307. //FIXME: Hack, when should the "i:type" be written?
  308. //Not used in case of enums
  309. writer.WriteXmlnsAttribute ("i", XmlSchema.InstanceNamespace);
  310. return;
  311. }
  312. /* Different names */
  313. known_types.Add (rootType);
  314. instName = KnownTypeCollection.GetPredefinedTypeName (graph.GetType ());
  315. if (instName == QName.Empty)
  316. /* Not a primitive type */
  317. instName = graph_qname;
  318. else
  319. /* FIXME: Hack, .. see test WriteObject7 () */
  320. instName = new QName (instName.Name, XmlSchema.Namespace);
  321. // output xsi:type as rootType is not equivalent to the graph's type.
  322. writer.WriteStartAttribute ("i", "type", XmlSchema.InstanceNamespace);
  323. writer.WriteQualifiedName (instName.Name, instName.Namespace);
  324. writer.WriteEndAttribute ();
  325. }
  326. public override void WriteEndObject (XmlDictionaryWriter writer)
  327. {
  328. writer.WriteEndElement ();
  329. }
  330. // SP1
  331. public override void WriteEndObject (XmlWriter writer)
  332. {
  333. WriteEndObject (XmlDictionaryWriter.CreateDictionaryWriter (writer));
  334. }
  335. }
  336. }
  337. #endif