SyndicationElementExtension.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Syndication
  5. {
  6. using System.IO;
  7. using System.Runtime;
  8. using System.Runtime.Serialization;
  9. using System.Xml;
  10. using System.Xml.Serialization;
  11. using System.Runtime.CompilerServices;
  12. [TypeForwardedFrom("System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
  13. public class SyndicationElementExtension
  14. {
  15. XmlBuffer buffer;
  16. int bufferElementIndex;
  17. // extensionData and extensionDataWriter are only present on the send side
  18. object extensionData;
  19. ExtensionDataWriter extensionDataWriter;
  20. string outerName;
  21. string outerNamespace;
  22. public SyndicationElementExtension(XmlReader xmlReader)
  23. {
  24. if (xmlReader == null)
  25. {
  26. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("xmlReader");
  27. }
  28. SyndicationFeedFormatter.MoveToStartElement(xmlReader);
  29. this.outerName = xmlReader.LocalName;
  30. this.outerNamespace = xmlReader.NamespaceURI;
  31. this.buffer = new XmlBuffer(int.MaxValue);
  32. using (XmlDictionaryWriter writer = this.buffer.OpenSection(XmlDictionaryReaderQuotas.Max))
  33. {
  34. writer.WriteStartElement(Rss20Constants.ExtensionWrapperTag);
  35. writer.WriteNode(xmlReader, false);
  36. writer.WriteEndElement();
  37. }
  38. buffer.CloseSection();
  39. buffer.Close();
  40. this.bufferElementIndex = 0;
  41. }
  42. public SyndicationElementExtension(object dataContractExtension)
  43. : this(dataContractExtension, (XmlObjectSerializer) null)
  44. {
  45. }
  46. public SyndicationElementExtension(object dataContractExtension, XmlObjectSerializer dataContractSerializer)
  47. : this(null, null, dataContractExtension, dataContractSerializer)
  48. {
  49. }
  50. public SyndicationElementExtension(string outerName, string outerNamespace, object dataContractExtension)
  51. : this(outerName, outerNamespace, dataContractExtension, null)
  52. {
  53. }
  54. public SyndicationElementExtension(string outerName, string outerNamespace, object dataContractExtension, XmlObjectSerializer dataContractSerializer)
  55. {
  56. if (dataContractExtension == null)
  57. {
  58. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("dataContractExtension");
  59. }
  60. if (outerName == string.Empty)
  61. {
  62. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.OuterNameOfElementExtensionEmpty));
  63. }
  64. if (dataContractSerializer == null)
  65. {
  66. dataContractSerializer = new DataContractSerializer(dataContractExtension.GetType());
  67. }
  68. this.outerName = outerName;
  69. this.outerNamespace = outerNamespace;
  70. this.extensionData = dataContractExtension;
  71. this.extensionDataWriter = new ExtensionDataWriter(this.extensionData, dataContractSerializer, this.outerName, this.outerNamespace);
  72. }
  73. public SyndicationElementExtension(object xmlSerializerExtension, XmlSerializer serializer)
  74. {
  75. if (xmlSerializerExtension == null)
  76. {
  77. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("xmlSerializerExtension");
  78. }
  79. if (serializer == null)
  80. {
  81. serializer = new XmlSerializer(xmlSerializerExtension.GetType());
  82. }
  83. this.extensionData = xmlSerializerExtension;
  84. this.extensionDataWriter = new ExtensionDataWriter(this.extensionData, serializer);
  85. }
  86. internal SyndicationElementExtension(XmlBuffer buffer, int bufferElementIndex, string outerName, string outerNamespace)
  87. {
  88. this.buffer = buffer;
  89. this.bufferElementIndex = bufferElementIndex;
  90. this.outerName = outerName;
  91. this.outerNamespace = outerNamespace;
  92. }
  93. public string OuterName
  94. {
  95. get
  96. {
  97. if (this.outerName == null)
  98. {
  99. EnsureOuterNameAndNs();
  100. }
  101. return this.outerName;
  102. }
  103. }
  104. public string OuterNamespace
  105. {
  106. get
  107. {
  108. if (this.outerName == null)
  109. {
  110. EnsureOuterNameAndNs();
  111. }
  112. return this.outerNamespace;
  113. }
  114. }
  115. public TExtension GetObject<TExtension>()
  116. {
  117. return GetObject<TExtension>(new DataContractSerializer(typeof(TExtension)));
  118. }
  119. public TExtension GetObject<TExtension>(XmlObjectSerializer serializer)
  120. {
  121. if (serializer == null)
  122. {
  123. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
  124. }
  125. if (this.extensionData != null && typeof(TExtension).IsAssignableFrom(extensionData.GetType()))
  126. {
  127. return (TExtension) this.extensionData;
  128. }
  129. using (XmlReader reader = GetReader())
  130. {
  131. return (TExtension) serializer.ReadObject(reader, false);
  132. }
  133. }
  134. public TExtension GetObject<TExtension>(XmlSerializer serializer)
  135. {
  136. if (serializer == null)
  137. {
  138. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
  139. }
  140. if (this.extensionData != null && typeof(TExtension).IsAssignableFrom(extensionData.GetType()))
  141. {
  142. return (TExtension) this.extensionData;
  143. }
  144. using (XmlReader reader = GetReader())
  145. {
  146. return (TExtension) serializer.Deserialize(reader);
  147. }
  148. }
  149. public XmlReader GetReader()
  150. {
  151. this.EnsureBuffer();
  152. XmlReader reader = this.buffer.GetReader(0);
  153. int index = 0;
  154. reader.ReadStartElement(Rss20Constants.ExtensionWrapperTag);
  155. while (reader.IsStartElement())
  156. {
  157. if (index == this.bufferElementIndex)
  158. {
  159. break;
  160. }
  161. ++index;
  162. reader.Skip();
  163. }
  164. return reader;
  165. }
  166. public void WriteTo(XmlWriter writer)
  167. {
  168. if (writer == null)
  169. {
  170. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer");
  171. }
  172. if (this.extensionDataWriter != null)
  173. {
  174. this.extensionDataWriter.WriteTo(writer);
  175. }
  176. else
  177. {
  178. using (XmlReader reader = GetReader())
  179. {
  180. writer.WriteNode(reader, false);
  181. }
  182. }
  183. }
  184. void EnsureBuffer()
  185. {
  186. if (this.buffer == null)
  187. {
  188. this.buffer = new XmlBuffer(int.MaxValue);
  189. using (XmlDictionaryWriter writer = this.buffer.OpenSection(XmlDictionaryReaderQuotas.Max))
  190. {
  191. writer.WriteStartElement(Rss20Constants.ExtensionWrapperTag);
  192. this.WriteTo(writer);
  193. writer.WriteEndElement();
  194. }
  195. buffer.CloseSection();
  196. buffer.Close();
  197. this.bufferElementIndex = 0;
  198. }
  199. }
  200. void EnsureOuterNameAndNs()
  201. {
  202. Fx.Assert(this.extensionDataWriter != null, "outer name is null only for datacontract and xmlserializer cases");
  203. this.extensionDataWriter.ComputeOuterNameAndNs(out this.outerName, out this.outerNamespace);
  204. }
  205. // this class holds the extension data and the associated serializer (either DataContractSerializer or XmlSerializer but not both)
  206. class ExtensionDataWriter
  207. {
  208. readonly XmlObjectSerializer dataContractSerializer;
  209. readonly object extensionData;
  210. readonly string outerName;
  211. readonly string outerNamespace;
  212. readonly XmlSerializer xmlSerializer;
  213. public ExtensionDataWriter(object extensionData, XmlObjectSerializer dataContractSerializer, string outerName, string outerNamespace)
  214. {
  215. Fx.Assert(extensionData != null && dataContractSerializer != null, "null check");
  216. this.dataContractSerializer = dataContractSerializer;
  217. this.extensionData = extensionData;
  218. this.outerName = outerName;
  219. this.outerNamespace = outerNamespace;
  220. }
  221. public ExtensionDataWriter(object extensionData, XmlSerializer serializer)
  222. {
  223. Fx.Assert(extensionData != null && serializer != null, "null check");
  224. this.xmlSerializer = serializer;
  225. this.extensionData = extensionData;
  226. }
  227. public void WriteTo(XmlWriter writer)
  228. {
  229. if (this.xmlSerializer != null)
  230. {
  231. Fx.Assert((this.dataContractSerializer == null && this.outerName == null && this.outerNamespace == null), "Xml serializer cannot have outer name, ns");
  232. this.xmlSerializer.Serialize(writer, this.extensionData);
  233. }
  234. else
  235. {
  236. Fx.Assert(this.xmlSerializer == null, "Xml serializer cannot be configured");
  237. if (this.outerName != null)
  238. {
  239. writer.WriteStartElement(outerName, outerNamespace);
  240. this.dataContractSerializer.WriteObjectContent(writer, this.extensionData);
  241. writer.WriteEndElement();
  242. }
  243. else
  244. {
  245. this.dataContractSerializer.WriteObject(writer, this.extensionData);
  246. }
  247. }
  248. }
  249. internal void ComputeOuterNameAndNs(out string name, out string ns)
  250. {
  251. if (this.outerName != null)
  252. {
  253. Fx.Assert(this.xmlSerializer == null, "outer name is not null for data contract extension only");
  254. name = this.outerName;
  255. ns = this.outerNamespace;
  256. }
  257. else if (this.dataContractSerializer != null)
  258. {
  259. Fx.Assert(this.xmlSerializer == null, "only one of xmlserializer or datacontract serializer can be present");
  260. XsdDataContractExporter dcExporter = new XsdDataContractExporter();
  261. XmlQualifiedName qName = dcExporter.GetRootElementName(this.extensionData.GetType());
  262. if (qName != null)
  263. {
  264. name = qName.Name;
  265. ns = qName.Namespace;
  266. }
  267. else
  268. {
  269. // this can happen if an IXmlSerializable type is specified with IsAny=true
  270. ReadOuterNameAndNs(out name, out ns);
  271. }
  272. }
  273. else
  274. {
  275. Fx.Assert(this.dataContractSerializer == null, "only one of xmlserializer or datacontract serializer can be present");
  276. XmlReflectionImporter importer = new XmlReflectionImporter();
  277. XmlTypeMapping typeMapping = importer.ImportTypeMapping(this.extensionData.GetType());
  278. if (typeMapping != null && !string.IsNullOrEmpty(typeMapping.ElementName))
  279. {
  280. name = typeMapping.ElementName;
  281. ns = typeMapping.Namespace;
  282. }
  283. else
  284. {
  285. // this can happen if an IXmlSerializable type is specified with IsAny=true
  286. ReadOuterNameAndNs(out name, out ns);
  287. }
  288. }
  289. }
  290. internal void ReadOuterNameAndNs(out string name, out string ns)
  291. {
  292. using (MemoryStream stream = new MemoryStream())
  293. {
  294. using (XmlWriter writer = XmlWriter.Create(stream))
  295. {
  296. this.WriteTo(writer);
  297. }
  298. stream.Seek(0, SeekOrigin.Begin);
  299. using (XmlReader reader = XmlReader.Create(stream))
  300. {
  301. SyndicationFeedFormatter.MoveToStartElement(reader);
  302. name = reader.LocalName;
  303. ns = reader.NamespaceURI;
  304. }
  305. }
  306. }
  307. }
  308. }
  309. }