XmlProtoSerializer.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #if (FEAT_SERVICEMODEL && PLAT_XMLSERIALIZER) || (SILVERLIGHT && !PHONE7)
  2. using System.IO;
  3. using System.Runtime.Serialization;
  4. using System.Xml;
  5. using ProtoBuf.Meta;
  6. using System;
  7. namespace ProtoBuf.ServiceModel
  8. {
  9. /// <summary>
  10. /// An xml object serializer that can embed protobuf data in a base-64 hunk (looking like a byte[])
  11. /// </summary>
  12. public sealed class XmlProtoSerializer : XmlObjectSerializer
  13. {
  14. private readonly TypeModel model;
  15. private readonly int key;
  16. private readonly bool isList, isEnum;
  17. private readonly Type type;
  18. internal XmlProtoSerializer(TypeModel model, int key, Type type, bool isList)
  19. {
  20. if (model == null) throw new ArgumentNullException(nameof(model));
  21. if (key < 0) throw new ArgumentOutOfRangeException(nameof(key));
  22. if (type == null) throw new ArgumentOutOfRangeException(nameof(type));
  23. this.model = model;
  24. this.key = key;
  25. this.isList = isList;
  26. this.type = type;
  27. this.isEnum = Helpers.IsEnum(type);
  28. }
  29. /// <summary>
  30. /// Attempt to create a new serializer for the given model and type
  31. /// </summary>
  32. /// <returns>A new serializer instance if the type is recognised by the model; null otherwise</returns>
  33. public static XmlProtoSerializer TryCreate(TypeModel model, Type type)
  34. {
  35. if (model == null) throw new ArgumentNullException(nameof(model));
  36. if (type == null) throw new ArgumentNullException(nameof(type));
  37. bool isList;
  38. int key = GetKey(model, ref type, out isList);
  39. if (key >= 0)
  40. {
  41. return new XmlProtoSerializer(model, key, type, isList);
  42. }
  43. return null;
  44. }
  45. /// <summary>
  46. /// Creates a new serializer for the given model and type
  47. /// </summary>
  48. public XmlProtoSerializer(TypeModel model, Type type)
  49. {
  50. if (model == null) throw new ArgumentNullException(nameof(model));
  51. if (type == null) throw new ArgumentNullException(nameof(type));
  52. key = GetKey(model, ref type, out isList);
  53. this.model = model;
  54. this.type = type;
  55. this.isEnum = Helpers.IsEnum(type);
  56. if (key < 0) throw new ArgumentOutOfRangeException(nameof(type), "Type not recognised by the model: " + type.FullName);
  57. }
  58. static int GetKey(TypeModel model, ref Type type, out bool isList)
  59. {
  60. if (model != null && type != null)
  61. {
  62. int key = model.GetKey(ref type);
  63. if (key >= 0)
  64. {
  65. isList = false;
  66. return key;
  67. }
  68. Type itemType = TypeModel.GetListItemType(model, type);
  69. if (itemType != null)
  70. {
  71. key = model.GetKey(ref itemType);
  72. if (key >= 0)
  73. {
  74. isList = true;
  75. return key;
  76. }
  77. }
  78. }
  79. isList = false;
  80. return -1;
  81. }
  82. /// <summary>
  83. /// Ends an object in the output
  84. /// </summary>
  85. public override void WriteEndObject(System.Xml.XmlDictionaryWriter writer)
  86. {
  87. if (writer == null) throw new ArgumentNullException(nameof(writer));
  88. writer.WriteEndElement();
  89. }
  90. /// <summary>
  91. /// Begins an object in the output
  92. /// </summary>
  93. public override void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object graph)
  94. {
  95. if (writer == null) throw new ArgumentNullException(nameof(writer));
  96. writer.WriteStartElement(PROTO_ELEMENT);
  97. }
  98. private const string PROTO_ELEMENT = "proto";
  99. /// <summary>
  100. /// Writes the body of an object in the output
  101. /// </summary>
  102. public override void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object graph)
  103. {
  104. if (writer == null) throw new ArgumentNullException(nameof(writer));
  105. if (graph == null)
  106. {
  107. writer.WriteAttributeString("nil", "true");
  108. }
  109. else
  110. {
  111. using (MemoryStream ms = new MemoryStream())
  112. {
  113. if (isList)
  114. {
  115. model.Serialize(ms, graph, null);
  116. }
  117. else
  118. {
  119. using (ProtoWriter protoWriter = new ProtoWriter(ms, model, null))
  120. {
  121. model.Serialize(key, graph, protoWriter);
  122. }
  123. }
  124. byte[] buffer = ms.GetBuffer();
  125. writer.WriteBase64(buffer, 0, (int)ms.Length);
  126. }
  127. }
  128. }
  129. /// <summary>
  130. /// Indicates whether this is the start of an object we are prepared to handle
  131. /// </summary>
  132. public override bool IsStartObject(System.Xml.XmlDictionaryReader reader)
  133. {
  134. if (reader == null) throw new ArgumentNullException(nameof(reader));
  135. reader.MoveToContent();
  136. return reader.NodeType == System.Xml.XmlNodeType.Element && reader.Name == PROTO_ELEMENT;
  137. }
  138. /// <summary>
  139. /// Reads the body of an object
  140. /// </summary>
  141. public override object ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName)
  142. {
  143. if (reader == null) throw new ArgumentNullException(nameof(reader));
  144. reader.MoveToContent();
  145. bool isSelfClosed = reader.IsEmptyElement, isNil = reader.GetAttribute("nil") == "true";
  146. reader.ReadStartElement(PROTO_ELEMENT);
  147. // explicitly null
  148. if (isNil)
  149. {
  150. if(!isSelfClosed) reader.ReadEndElement();
  151. return null;
  152. }
  153. if(isSelfClosed) // no real content
  154. {
  155. if (isList || isEnum)
  156. {
  157. return model.Deserialize(Stream.Null, null, type, null);
  158. }
  159. ProtoReader protoReader = null;
  160. try
  161. {
  162. protoReader = ProtoReader.Create(Stream.Null, model, null, ProtoReader.TO_EOF);
  163. return model.Deserialize(key, null, protoReader);
  164. }
  165. finally
  166. {
  167. ProtoReader.Recycle(protoReader);
  168. }
  169. }
  170. object result;
  171. Helpers.DebugAssert(reader.CanReadBinaryContent, "CanReadBinaryContent");
  172. using (MemoryStream ms = new MemoryStream(reader.ReadContentAsBase64()))
  173. {
  174. if (isList || isEnum)
  175. {
  176. result = model.Deserialize(ms, null, type, null);
  177. }
  178. else
  179. {
  180. ProtoReader protoReader = null;
  181. try
  182. {
  183. protoReader = ProtoReader.Create(ms, model, null, ProtoReader.TO_EOF);
  184. result = model.Deserialize(key, null, protoReader);
  185. }
  186. finally
  187. {
  188. ProtoReader.Recycle(protoReader);
  189. }
  190. }
  191. }
  192. reader.ReadEndElement();
  193. return result;
  194. }
  195. }
  196. }
  197. #endif