MessageFormatFactory.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. using System;
  2. using System.IO;
  3. using System.Xml;
  4. using System.Text;
  5. namespace Google.ProtocolBuffers.Serialization
  6. {
  7. /// <summary>
  8. /// Extensions and helpers to abstract the reading/writing of messages by a client-specified content type.
  9. /// </summary>
  10. public static class MessageFormatFactory
  11. {
  12. /// <summary>
  13. /// Constructs an ICodedInputStream from the input stream based on the contentType provided
  14. /// </summary>
  15. /// <param name="options">Options specific to reading this message and/or content type</param>
  16. /// <param name="contentType">The mime type of the input stream content</param>
  17. /// <param name="input">The stream to read the message from</param>
  18. /// <returns>The ICodedInputStream that can be given to the IBuilder.MergeFrom(...) method</returns>
  19. public static ICodedInputStream CreateInputStream(MessageFormatOptions options, string contentType, Stream input)
  20. {
  21. FormatType inputType = ContentTypeToFormat(contentType, options.DefaultContentType);
  22. ICodedInputStream codedInput;
  23. if (inputType == FormatType.ProtoBuffer)
  24. {
  25. codedInput = CodedInputStream.CreateInstance(input);
  26. }
  27. else if (inputType == FormatType.Json)
  28. {
  29. JsonFormatReader reader = JsonFormatReader.CreateInstance(input);
  30. codedInput = reader.ReadStartMessage();
  31. }
  32. else if (inputType == FormatType.Xml)
  33. {
  34. XmlFormatReader reader = XmlFormatReader.CreateInstance(input);
  35. reader.RootElementName = options.XmlReaderRootElementName;
  36. reader.Options = options.XmlReaderOptions;
  37. codedInput = reader.ReadStartMessage();
  38. }
  39. else
  40. throw new NotSupportedException();
  41. return codedInput;
  42. }
  43. /// <summary>
  44. /// Merges the message from the input stream based on the contentType provided
  45. /// </summary>
  46. /// <typeparam name="TBuilder">A type derived from IBuilderLite</typeparam>
  47. /// <param name="builder">An instance of a message builder</param>
  48. /// <param name="options">Options specific to reading this message and/or content type</param>
  49. /// <param name="contentType">The mime type of the input stream content</param>
  50. /// <param name="input">The stream to read the message from</param>
  51. /// <returns>The same builder instance that was supplied in the builder parameter</returns>
  52. public static TBuilder MergeFrom<TBuilder>(this TBuilder builder, MessageFormatOptions options, string contentType, Stream input) where TBuilder : IBuilderLite
  53. {
  54. ICodedInputStream codedInput = CreateInputStream(options, contentType, input);
  55. return (TBuilder)builder.WeakMergeFrom(codedInput, options.ExtensionRegistry);
  56. }
  57. /// <summary>
  58. /// Writes the message instance to the stream using the content type provided
  59. /// </summary>
  60. /// <param name="options">Options specific to writing this message and/or content type</param>
  61. /// <param name="contentType">The mime type of the content to be written</param>
  62. /// <param name="output">The stream to write the message to</param>
  63. /// <remarks> If you do not dispose of ICodedOutputStream some formats may yield incomplete output </remarks>
  64. public static ICodedOutputStream CreateOutputStream(MessageFormatOptions options, string contentType, Stream output)
  65. {
  66. FormatType outputType = ContentTypeToFormat(contentType, options.DefaultContentType);
  67. ICodedOutputStream codedOutput;
  68. if (outputType == FormatType.ProtoBuffer)
  69. {
  70. codedOutput = CodedOutputStream.CreateInstance(output);
  71. }
  72. else if (outputType == FormatType.Json)
  73. {
  74. JsonFormatWriter writer = JsonFormatWriter.CreateInstance(output);
  75. if (options.FormattedOutput)
  76. {
  77. writer.Formatted();
  78. }
  79. writer.StartMessage();
  80. codedOutput = writer;
  81. }
  82. else if (outputType == FormatType.Xml)
  83. {
  84. XmlFormatWriter writer;
  85. if (options.FormattedOutput)
  86. {
  87. writer = XmlFormatWriter.CreateInstance(output);
  88. }
  89. else
  90. {
  91. XmlWriterSettings settings = new XmlWriterSettings()
  92. {
  93. CheckCharacters = false,
  94. NewLineHandling = NewLineHandling.Entitize,
  95. OmitXmlDeclaration = true,
  96. Encoding = Encoding.UTF8,
  97. Indent = true,
  98. IndentChars = " ",
  99. NewLineChars = Environment.NewLine,
  100. };
  101. writer = XmlFormatWriter.CreateInstance(XmlWriter.Create(output, settings));
  102. }
  103. writer.RootElementName = options.XmlWriterRootElementName;
  104. writer.Options = options.XmlWriterOptions;
  105. writer.StartMessage();
  106. codedOutput = writer;
  107. }
  108. else
  109. throw new NotSupportedException();
  110. return codedOutput;
  111. }
  112. /// <summary>
  113. /// Writes the message instance to the stream using the content type provided
  114. /// </summary>
  115. /// <param name="message">An instance of a message</param>
  116. /// <param name="options">Options specific to writing this message and/or content type</param>
  117. /// <param name="contentType">The mime type of the content to be written</param>
  118. /// <param name="output">The stream to write the message to</param>
  119. public static void WriteTo(this IMessageLite message, MessageFormatOptions options, string contentType, Stream output)
  120. {
  121. using (ICodedOutputStream codedOutput = CreateOutputStream(options, contentType, output))
  122. {
  123. message.WriteTo(codedOutput);
  124. // This is effectivly done by Dispose(); however, if you need to finalize a message
  125. // without disposing the underlying stream, this is the only way to do it.
  126. if (codedOutput is AbstractWriter)
  127. ((AbstractWriter)codedOutput).EndMessage();
  128. codedOutput.Flush();
  129. }
  130. }
  131. enum FormatType { ProtoBuffer, Json, Xml };
  132. private static FormatType ContentTypeToFormat(string contentType, string defaultType)
  133. {
  134. switch ((contentType ?? String.Empty).Split(';')[0].Trim().ToLower())
  135. {
  136. case "application/json":
  137. case "application/x-json":
  138. case "application/x-javascript":
  139. case "text/javascript":
  140. case "text/x-javascript":
  141. case "text/x-json":
  142. case "text/json":
  143. {
  144. return FormatType.Json;
  145. }
  146. case "text/xml":
  147. case "application/xml":
  148. {
  149. return FormatType.Xml;
  150. }
  151. case "application/binary":
  152. case "application/x-protobuf":
  153. case "application/vnd.google.protobuf":
  154. {
  155. return FormatType.ProtoBuffer;
  156. }
  157. case "":
  158. case null:
  159. if (!String.IsNullOrEmpty(defaultType))
  160. {
  161. return ContentTypeToFormat(defaultType, null);
  162. }
  163. break;
  164. }
  165. throw new ArgumentOutOfRangeException("contentType");
  166. }
  167. }
  168. }