using System; using System.IO; using System.Xml; using System.Text; namespace Google.ProtocolBuffers.Serialization { /// /// Extensions and helpers to abstract the reading/writing of messages by a client-specified content type. /// public static class MessageFormatFactory { /// /// Constructs an ICodedInputStream from the input stream based on the contentType provided /// /// Options specific to reading this message and/or content type /// The mime type of the input stream content /// The stream to read the message from /// The ICodedInputStream that can be given to the IBuilder.MergeFrom(...) method public static ICodedInputStream CreateInputStream(MessageFormatOptions options, string contentType, Stream input) { FormatType inputType = ContentTypeToFormat(contentType, options.DefaultContentType); ICodedInputStream codedInput; if (inputType == FormatType.ProtoBuffer) { codedInput = CodedInputStream.CreateInstance(input); } else if (inputType == FormatType.Json) { JsonFormatReader reader = JsonFormatReader.CreateInstance(input); codedInput = reader.ReadStartMessage(); } else if (inputType == FormatType.Xml) { XmlFormatReader reader = XmlFormatReader.CreateInstance(input); reader.RootElementName = options.XmlReaderRootElementName; reader.Options = options.XmlReaderOptions; codedInput = reader.ReadStartMessage(); } else throw new NotSupportedException(); return codedInput; } /// /// Merges the message from the input stream based on the contentType provided /// /// A type derived from IBuilderLite /// An instance of a message builder /// Options specific to reading this message and/or content type /// The mime type of the input stream content /// The stream to read the message from /// The same builder instance that was supplied in the builder parameter public static TBuilder MergeFrom(this TBuilder builder, MessageFormatOptions options, string contentType, Stream input) where TBuilder : IBuilderLite { ICodedInputStream codedInput = CreateInputStream(options, contentType, input); return (TBuilder)builder.WeakMergeFrom(codedInput, options.ExtensionRegistry); } /// /// Writes the message instance to the stream using the content type provided /// /// Options specific to writing this message and/or content type /// The mime type of the content to be written /// The stream to write the message to /// If you do not dispose of ICodedOutputStream some formats may yield incomplete output public static ICodedOutputStream CreateOutputStream(MessageFormatOptions options, string contentType, Stream output) { FormatType outputType = ContentTypeToFormat(contentType, options.DefaultContentType); ICodedOutputStream codedOutput; if (outputType == FormatType.ProtoBuffer) { codedOutput = CodedOutputStream.CreateInstance(output); } else if (outputType == FormatType.Json) { JsonFormatWriter writer = JsonFormatWriter.CreateInstance(output); if (options.FormattedOutput) { writer.Formatted(); } writer.StartMessage(); codedOutput = writer; } else if (outputType == FormatType.Xml) { XmlFormatWriter writer; if (options.FormattedOutput) { writer = XmlFormatWriter.CreateInstance(output); } else { XmlWriterSettings settings = new XmlWriterSettings() { CheckCharacters = false, NewLineHandling = NewLineHandling.Entitize, OmitXmlDeclaration = true, Encoding = Encoding.UTF8, Indent = true, IndentChars = " ", NewLineChars = Environment.NewLine, }; writer = XmlFormatWriter.CreateInstance(XmlWriter.Create(output, settings)); } writer.RootElementName = options.XmlWriterRootElementName; writer.Options = options.XmlWriterOptions; writer.StartMessage(); codedOutput = writer; } else throw new NotSupportedException(); return codedOutput; } /// /// Writes the message instance to the stream using the content type provided /// /// An instance of a message /// Options specific to writing this message and/or content type /// The mime type of the content to be written /// The stream to write the message to public static void WriteTo(this IMessageLite message, MessageFormatOptions options, string contentType, Stream output) { using (ICodedOutputStream codedOutput = CreateOutputStream(options, contentType, output)) { message.WriteTo(codedOutput); // This is effectivly done by Dispose(); however, if you need to finalize a message // without disposing the underlying stream, this is the only way to do it. if (codedOutput is AbstractWriter) ((AbstractWriter)codedOutput).EndMessage(); codedOutput.Flush(); } } enum FormatType { ProtoBuffer, Json, Xml }; private static FormatType ContentTypeToFormat(string contentType, string defaultType) { switch ((contentType ?? String.Empty).Split(';')[0].Trim().ToLower()) { case "application/json": case "application/x-json": case "application/x-javascript": case "text/javascript": case "text/x-javascript": case "text/x-json": case "text/json": { return FormatType.Json; } case "text/xml": case "application/xml": { return FormatType.Xml; } case "application/binary": case "application/x-protobuf": case "application/vnd.google.protobuf": { return FormatType.ProtoBuffer; } case "": case null: if (!String.IsNullOrEmpty(defaultType)) { return ContentTypeToFormat(defaultType, null); } break; } throw new ArgumentOutOfRangeException("contentType"); } } }