MessageParser.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. #region Copyright notice and license
  2. // Protocol Buffers - Google's data interchange format
  3. // Copyright 2015 Google Inc. All rights reserved.
  4. // https://developers.google.com/protocol-buffers/
  5. //
  6. // Redistribution and use in source and binary forms, with or without
  7. // modification, are permitted provided that the following conditions are
  8. // met:
  9. //
  10. // * Redistributions of source code must retain the above copyright
  11. // notice, this list of conditions and the following disclaimer.
  12. // * Redistributions in binary form must reproduce the above
  13. // copyright notice, this list of conditions and the following disclaimer
  14. // in the documentation and/or other materials provided with the
  15. // distribution.
  16. // * Neither the name of Google Inc. nor the names of its
  17. // contributors may be used to endorse or promote products derived from
  18. // this software without specific prior written permission.
  19. //
  20. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. #endregion
  32. using System;
  33. using System.IO;
  34. namespace Google.Protobuf
  35. {
  36. /// <summary>
  37. /// A general message parser, typically used by reflection-based code as all the methods
  38. /// return simple <see cref="IMessage"/>.
  39. /// </summary>
  40. public class MessageParser
  41. {
  42. private Func<IMessage> factory;
  43. // TODO: When we use a C# 7.1 compiler, make this private protected.
  44. internal bool DiscardUnknownFields { get; }
  45. internal ExtensionRegistry Extensions { get; }
  46. internal MessageParser(Func<IMessage> factory, bool discardUnknownFields, ExtensionRegistry extensions)
  47. {
  48. this.factory = factory;
  49. DiscardUnknownFields = discardUnknownFields;
  50. Extensions = extensions;
  51. }
  52. /// <summary>
  53. /// Creates a template instance ready for population.
  54. /// </summary>
  55. /// <returns>An empty message.</returns>
  56. internal IMessage CreateTemplate()
  57. {
  58. return factory();
  59. }
  60. /// <summary>
  61. /// Parses a message from a byte array.
  62. /// </summary>
  63. /// <param name="data">The byte array containing the message. Must not be null.</param>
  64. /// <returns>The newly parsed message.</returns>
  65. public IMessage ParseFrom(byte[] data)
  66. {
  67. IMessage message = factory();
  68. message.MergeFrom(data, DiscardUnknownFields, Extensions);
  69. return message;
  70. }
  71. /// <summary>
  72. /// Parses a message from a byte array slice.
  73. /// </summary>
  74. /// <param name="data">The byte array containing the message. Must not be null.</param>
  75. /// <param name="offset">The offset of the slice to parse.</param>
  76. /// <param name="length">The length of the slice to parse.</param>
  77. /// <returns>The newly parsed message.</returns>
  78. public IMessage ParseFrom(byte[] data, int offset, int length)
  79. {
  80. IMessage message = factory();
  81. message.MergeFrom(data, offset, length, DiscardUnknownFields, Extensions);
  82. return message;
  83. }
  84. /// <summary>
  85. /// Parses a message from the given byte string.
  86. /// </summary>
  87. /// <param name="data">The data to parse.</param>
  88. /// <returns>The parsed message.</returns>
  89. public IMessage ParseFrom(ByteString data)
  90. {
  91. IMessage message = factory();
  92. message.MergeFrom(data, DiscardUnknownFields, Extensions);
  93. return message;
  94. }
  95. /// <summary>
  96. /// Parses a message from the given stream.
  97. /// </summary>
  98. /// <param name="input">The stream to parse.</param>
  99. /// <returns>The parsed message.</returns>
  100. public IMessage ParseFrom(Stream input)
  101. {
  102. IMessage message = factory();
  103. message.MergeFrom(input, DiscardUnknownFields, Extensions);
  104. return message;
  105. }
  106. /// <summary>
  107. /// Parses a length-delimited message from the given stream.
  108. /// </summary>
  109. /// <remarks>
  110. /// The stream is expected to contain a length and then the data. Only the amount of data
  111. /// specified by the length will be consumed.
  112. /// </remarks>
  113. /// <param name="input">The stream to parse.</param>
  114. /// <returns>The parsed message.</returns>
  115. public IMessage ParseDelimitedFrom(Stream input)
  116. {
  117. IMessage message = factory();
  118. message.MergeDelimitedFrom(input, DiscardUnknownFields, Extensions);
  119. return message;
  120. }
  121. /// <summary>
  122. /// Parses a message from the given coded input stream.
  123. /// </summary>
  124. /// <param name="input">The stream to parse.</param>
  125. /// <returns>The parsed message.</returns>
  126. public IMessage ParseFrom(CodedInputStream input)
  127. {
  128. IMessage message = factory();
  129. MergeFrom(message, input);
  130. return message;
  131. }
  132. /// <summary>
  133. /// Parses a message from the given JSON.
  134. /// </summary>
  135. /// <param name="json">The JSON to parse.</param>
  136. /// <returns>The parsed message.</returns>
  137. /// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception>
  138. /// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
  139. public IMessage ParseJson(string json)
  140. {
  141. IMessage message = factory();
  142. JsonParser.Default.Merge(message, json);
  143. return message;
  144. }
  145. // TODO: When we're using a C# 7.1 compiler, make this private protected.
  146. internal void MergeFrom(IMessage message, CodedInputStream codedInput)
  147. {
  148. bool originalDiscard = codedInput.DiscardUnknownFields;
  149. try
  150. {
  151. codedInput.DiscardUnknownFields = DiscardUnknownFields;
  152. message.MergeFrom(codedInput);
  153. }
  154. finally
  155. {
  156. codedInput.DiscardUnknownFields = originalDiscard;
  157. }
  158. }
  159. /// <summary>
  160. /// Creates a new message parser which optionally discards unknown fields when parsing.
  161. /// </summary>
  162. /// <param name="discardUnknownFields">Whether or not to discard unknown fields when parsing.</param>
  163. /// <returns>A newly configured message parser.</returns>
  164. public MessageParser WithDiscardUnknownFields(bool discardUnknownFields) =>
  165. new MessageParser(factory, discardUnknownFields, Extensions);
  166. /// <summary>
  167. /// Creates a new message parser which registers extensions from the specified registry upon creating the message instance
  168. /// </summary>
  169. /// <param name="registry">The extensions to register</param>
  170. /// <returns>A newly configured message parser.</returns>
  171. public MessageParser WithExtensionRegistry(ExtensionRegistry registry) =>
  172. new MessageParser(factory, DiscardUnknownFields, registry);
  173. }
  174. /// <summary>
  175. /// A parser for a specific message type.
  176. /// </summary>
  177. /// <remarks>
  178. /// <p>
  179. /// This delegates most behavior to the
  180. /// <see cref="IMessage.MergeFrom"/> implementation within the original type, but
  181. /// provides convenient overloads to parse from a variety of sources.
  182. /// </p>
  183. /// <p>
  184. /// Most applications will never need to create their own instances of this type;
  185. /// instead, use the static <c>Parser</c> property of a generated message type to obtain a
  186. /// parser for that type.
  187. /// </p>
  188. /// </remarks>
  189. /// <typeparam name="T">The type of message to be parsed.</typeparam>
  190. public sealed class MessageParser<T> : MessageParser where T : IMessage<T>
  191. {
  192. // Implementation note: all the methods here *could* just delegate up to the base class and cast the result.
  193. // The current implementation avoids a virtual method call and a cast, which *may* be significant in some cases.
  194. // Benchmarking work is required to measure the significance - but it's only a few lines of code in any case.
  195. // The API wouldn't change anyway - just the implementation - so this work can be deferred.
  196. private readonly Func<T> factory;
  197. /// <summary>
  198. /// Creates a new parser.
  199. /// </summary>
  200. /// <remarks>
  201. /// The factory method is effectively an optimization over using a generic constraint
  202. /// to require a parameterless constructor: delegates are significantly faster to execute.
  203. /// </remarks>
  204. /// <param name="factory">Function to invoke when a new, empty message is required.</param>
  205. public MessageParser(Func<T> factory) : this(factory, false, null)
  206. {
  207. }
  208. internal MessageParser(Func<T> factory, bool discardUnknownFields, ExtensionRegistry extensions) : base(() => factory(), discardUnknownFields, extensions)
  209. {
  210. this.factory = factory;
  211. }
  212. /// <summary>
  213. /// Creates a template instance ready for population.
  214. /// </summary>
  215. /// <returns>An empty message.</returns>
  216. internal new T CreateTemplate()
  217. {
  218. return factory();
  219. }
  220. /// <summary>
  221. /// Parses a message from a byte array.
  222. /// </summary>
  223. /// <param name="data">The byte array containing the message. Must not be null.</param>
  224. /// <returns>The newly parsed message.</returns>
  225. public new T ParseFrom(byte[] data)
  226. {
  227. T message = factory();
  228. message.MergeFrom(data, DiscardUnknownFields, Extensions);
  229. return message;
  230. }
  231. /// <summary>
  232. /// Parses a message from a byte array slice.
  233. /// </summary>
  234. /// <param name="data">The byte array containing the message. Must not be null.</param>
  235. /// <param name="offset">The offset of the slice to parse.</param>
  236. /// <param name="length">The length of the slice to parse.</param>
  237. /// <returns>The newly parsed message.</returns>
  238. public new T ParseFrom(byte[] data, int offset, int length)
  239. {
  240. T message = factory();
  241. message.MergeFrom(data, offset, length, DiscardUnknownFields, Extensions);
  242. return message;
  243. }
  244. /// <summary>
  245. /// Parses a message from the given byte string.
  246. /// </summary>
  247. /// <param name="data">The data to parse.</param>
  248. /// <returns>The parsed message.</returns>
  249. public new T ParseFrom(ByteString data)
  250. {
  251. T message = factory();
  252. message.MergeFrom(data, DiscardUnknownFields, Extensions);
  253. return message;
  254. }
  255. /// <summary>
  256. /// Parses a message from the given stream.
  257. /// </summary>
  258. /// <param name="input">The stream to parse.</param>
  259. /// <returns>The parsed message.</returns>
  260. public new T ParseFrom(Stream input)
  261. {
  262. T message = factory();
  263. message.MergeFrom(input, DiscardUnknownFields, Extensions);
  264. return message;
  265. }
  266. /// <summary>
  267. /// Parses a length-delimited message from the given stream.
  268. /// </summary>
  269. /// <remarks>
  270. /// The stream is expected to contain a length and then the data. Only the amount of data
  271. /// specified by the length will be consumed.
  272. /// </remarks>
  273. /// <param name="input">The stream to parse.</param>
  274. /// <returns>The parsed message.</returns>
  275. public new T ParseDelimitedFrom(Stream input)
  276. {
  277. T message = factory();
  278. message.MergeDelimitedFrom(input, DiscardUnknownFields, Extensions);
  279. return message;
  280. }
  281. /// <summary>
  282. /// Parses a message from the given coded input stream.
  283. /// </summary>
  284. /// <param name="input">The stream to parse.</param>
  285. /// <returns>The parsed message.</returns>
  286. public new T ParseFrom(CodedInputStream input)
  287. {
  288. T message = factory();
  289. MergeFrom(message, input);
  290. return message;
  291. }
  292. /// <summary>
  293. /// Parses a message from the given JSON.
  294. /// </summary>
  295. /// <param name="json">The JSON to parse.</param>
  296. /// <returns>The parsed message.</returns>
  297. /// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception>
  298. /// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
  299. public new T ParseJson(string json)
  300. {
  301. T message = factory();
  302. JsonParser.Default.Merge(message, json);
  303. return message;
  304. }
  305. /// <summary>
  306. /// Creates a new message parser which optionally discards unknown fields when parsing.
  307. /// </summary>
  308. /// <param name="discardUnknownFields">Whether or not to discard unknown fields when parsing.</param>
  309. /// <returns>A newly configured message parser.</returns>
  310. public new MessageParser<T> WithDiscardUnknownFields(bool discardUnknownFields) =>
  311. new MessageParser<T>(factory, discardUnknownFields, Extensions);
  312. /// <summary>
  313. /// Creates a new message parser which registers extensions from the specified registry upon creating the message instance
  314. /// </summary>
  315. /// <param name="registry">The extensions to register</param>
  316. /// <returns>A newly configured message parser.</returns>
  317. public new MessageParser<T> WithExtensionRegistry(ExtensionRegistry registry) =>
  318. new MessageParser<T>(factory, DiscardUnknownFields, registry);
  319. }
  320. }