Commit 247c7534 authored by csharptest's avatar csharptest Committed by rogerk

merged issue-24

parents b6a14572 06963f90
......@@ -17,12 +17,6 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary> Constructs a new reader </summary>
protected AbstractReader() { MaxDepth = DefaultMaxDepth; }
/// <summary> Constructs a new child reader </summary>
protected AbstractReader(AbstractReader copyFrom)
{
_depth = copyFrom._depth + 1;
MaxDepth = copyFrom.MaxDepth;
}
/// <summary> Gets or sets the maximum recursion depth allowed </summary>
public int MaxDepth { get; set; }
......@@ -113,6 +107,16 @@ namespace Google.ProtocolBuffers.Serialization
/// </summary>
protected abstract bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry);
/// <summary>
/// Reads the root-message preamble specific to this formatter
/// </summary>
public abstract void ReadMessageStart();
/// <summary>
/// Reads the root-message close specific to this formatter
/// </summary>
public abstract void ReadMessageEnd();
/// <summary>
/// Merges the input stream into the provided IBuilderLite
/// </summary>
......
......@@ -11,10 +11,6 @@ namespace Google.ProtocolBuffers.Serialization
{
/// <summary> Constructs a new reader </summary>
protected AbstractTextReader() { }
/// <summary> Constructs a new child reader </summary>
protected AbstractTextReader(AbstractTextReader copyFrom)
: base(copyFrom)
{ }
/// <summary>
/// Reads a typed field as a string
......
......@@ -12,36 +12,39 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary>
/// Provides a base class for writers that performs some basic type dispatching
/// </summary>
public abstract class AbstractWriter : ICodedOutputStream, IDisposable
public abstract class AbstractWriter : ICodedOutputStream
{
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// Completes any pending write operations
/// </summary>
public void Dispose()
public virtual void Flush()
{
GC.SuppressFinalize(this);
Flush();
Dispose(true);
}
/// <summary>
/// Completes any pending write operations
/// Writes the message to the the formatted stream.
/// </summary>
public virtual void Flush()
{
}
public abstract void WriteMessage(IMessageLite message);
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// Used to write any nessary root-message preamble. After this call you can call
/// IMessageLite.MergeTo(...) and complete the message with a call to WriteMessageEnd().
/// These three calls are identical to just calling WriteMessage(message);
/// </summary>
protected virtual void Dispose(bool disposing)
{
}
/// <example>
/// AbstractWriter writer;
/// writer.WriteMessageStart();
/// message.WriteTo(writer);
/// writer.WriteMessageEnd();
/// // ... or, but not both ...
/// writer.WriteMessage(message);
/// </example>
public abstract void WriteMessageStart();
/// <summary>
/// Writes the message to the the formatted stream.
/// Used to complete a root-message previously started with a call to WriteMessageStart()
/// </summary>
public abstract void WriteMessage(IMessageLite message);
public abstract void WriteMessageEnd();
/// <summary>
/// Writes a Boolean value
......
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using Google.ProtocolBuffers.Descriptors;
......@@ -22,6 +22,18 @@ namespace Google.ProtocolBuffers.Serialization
_ready = _input.MoveNext();
}
/// <summary>
/// No-op
/// </summary>
public override void ReadMessageStart()
{ }
/// <summary>
/// No-op
/// </summary>
public override void ReadMessageEnd()
{ }
/// <summary>
/// Merges the contents of stream into the provided message builder
/// </summary>
......
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using Google.ProtocolBuffers.Descriptors;
......@@ -53,6 +53,19 @@ namespace Google.ProtocolBuffers.Serialization
message.WriteTo(this);
}
/// <summary>
/// No-op
/// </summary>
public override void WriteMessageStart()
{ }
/// <summary>
/// No-op
/// </summary>
public override void WriteMessageEnd()
{ }
/// <summary>
/// Writes a Boolean value
/// </summary>
......
using System;
using System.IO;
using System.Text;
namespace Google.ProtocolBuffers.Serialization.Http
{
/// <summary>
/// Allows reading messages from a name/value dictionary
/// </summary>
public class FormUrlEncodedReader : AbstractTextReader
{
private readonly TextReader _input;
private string _fieldName, _fieldValue;
private bool _ready;
/// <summary>
/// Creates a dictionary reader from an enumeration of KeyValuePair data, like an IDictionary
/// </summary>
FormUrlEncodedReader(TextReader input)
{
_input = input;
int ch = input.Peek();
if (ch == '?')
{
input.Read();
}
_ready = ReadNext();
}
#region CreateInstance overloads
/// <summary>
/// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message.
/// </summary>
public static FormUrlEncodedReader CreateInstance(Stream stream)
{
return new FormUrlEncodedReader(new StreamReader(stream, Encoding.UTF8, false));
}
/// <summary>
/// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message.
/// </summary>
public static FormUrlEncodedReader CreateInstance(byte[] bytes)
{
return new FormUrlEncodedReader(new StreamReader(new MemoryStream(bytes, false), Encoding.UTF8, false));
}
/// <summary>
/// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message.
/// </summary>
public static FormUrlEncodedReader CreateInstance(string text)
{
return new FormUrlEncodedReader(new StringReader(text));
}
/// <summary>
/// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message.
/// </summary>
public static FormUrlEncodedReader CreateInstance(TextReader input)
{
return new FormUrlEncodedReader(input);
}
#endregion
private bool ReadNext()
{
StringBuilder field = new StringBuilder(32);
StringBuilder value = new StringBuilder(64);
int ch;
while (-1 != (ch = _input.Read()) && ch != '=' && ch != '&')
{
field.Append((char)ch);
}
if (ch != -1 && ch != '&')
{
while (-1 != (ch = _input.Read()) && ch != '&')
{
value.Append((char)ch);
}
}
_fieldName = field.ToString();
_fieldValue = Uri.UnescapeDataString(value.Replace('+', ' ').ToString());
return !String.IsNullOrEmpty(_fieldName);
}
/// <summary>
/// No-op
/// </summary>
public override void ReadMessageStart()
{ }
/// <summary>
/// No-op
/// </summary>
public override void ReadMessageEnd()
{ }
/// <summary>
/// Merges the contents of stream into the provided message builder
/// </summary>
public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
{
builder.WeakMergeFrom(this, registry);
return builder;
}
/// <summary>
/// Causes the reader to skip past this field
/// </summary>
protected override void Skip()
{
_ready = ReadNext();
}
/// <summary>
/// Peeks at the next field in the input stream and returns what information is available.
/// </summary>
/// <remarks>
/// This may be called multiple times without actually reading the field. Only after the field
/// is either read, or skipped, should PeekNext return a different value.
/// </remarks>
protected override bool PeekNext(out string field)
{
field = _ready ? _fieldName : null;
return field != null;
}
/// <summary>
/// Returns true if it was able to read a String from the input
/// </summary>
protected override bool ReadAsText(ref string value, Type typeInfo)
{
if (_ready)
{
value = _fieldValue;
_ready = ReadNext();
return true;
}
return false;
}
/// <summary>
/// It's unlikely this will work for anything but text data as bytes UTF8 are transformed to text and back to bytes
/// </summary>
protected override ByteString DecodeBytes(string bytes)
{ return ByteString.CopyFromUtf8(bytes); }
/// <summary>
/// Not Supported
/// </summary>
public override bool ReadGroup(IBuilderLite value, ExtensionRegistry registry)
{ throw new NotSupportedException(); }
/// <summary>
/// Not Supported
/// </summary>
protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
{ throw new NotSupportedException(); }
}
}
\ No newline at end of file
using System;
using System.IO;
using System.Xml;
using System.Text;
namespace Google.ProtocolBuffers.Serialization.Http
{
/// <summary>
/// Extensions and helpers to abstract the reading/writing of messages by a client-specified content type.
/// </summary>
public static class MessageFormatFactory
{
/// <summary>
/// Constructs an ICodedInputStream from the input stream based on the contentType provided
/// </summary>
/// <param name="options">Options specific to reading this message and/or content type</param>
/// <param name="contentType">The mime type of the input stream content</param>
/// <param name="input">The stream to read the message from</param>
/// <returns>The ICodedInputStream that can be given to the IBuilder.MergeFrom(...) method</returns>
public static ICodedInputStream CreateInputStream(MessageFormatOptions options, string contentType, Stream input)
{
ICodedInputStream codedInput = ContentTypeToInputStream(contentType, options, input);
if (codedInput is XmlFormatReader)
{
XmlFormatReader reader = (XmlFormatReader)codedInput;
reader.RootElementName = options.XmlReaderRootElementName;
reader.Options = options.XmlReaderOptions;
}
return codedInput;
}
/// <summary>
/// Merges the message from the input stream based on the contentType provided
/// </summary>
/// <typeparam name="TBuilder">A type derived from IBuilderLite</typeparam>
/// <param name="builder">An instance of a message builder</param>
/// <param name="options">Options specific to reading this message and/or content type</param>
/// <param name="contentType">The mime type of the input stream content</param>
/// <param name="input">The stream to read the message from</param>
/// <returns>The same builder instance that was supplied in the builder parameter</returns>
public static TBuilder MergeFrom<TBuilder>(this TBuilder builder, MessageFormatOptions options, string contentType, Stream input) where TBuilder : IBuilderLite
{
ICodedInputStream codedInput = CreateInputStream(options, contentType, input);
codedInput.ReadMessageStart();
builder.WeakMergeFrom(codedInput, options.ExtensionRegistry);
codedInput.ReadMessageEnd();
return builder;
}
/// <summary>
/// Writes the message instance to the stream using the content type provided
/// </summary>
/// <param name="options">Options specific to writing this message and/or content type</param>
/// <param name="contentType">The mime type of the content to be written</param>
/// <param name="output">The stream to write the message to</param>
/// <remarks> If you do not dispose of ICodedOutputStream some formats may yield incomplete output </remarks>
public static ICodedOutputStream CreateOutputStream(MessageFormatOptions options, string contentType, Stream output)
{
ICodedOutputStream codedOutput = ContentTypeToOutputStream(contentType, options, output);
if (codedOutput is JsonFormatWriter)
{
JsonFormatWriter writer = (JsonFormatWriter)codedOutput;
if (options.FormattedOutput)
{
writer.Formatted();
}
}
else if (codedOutput is XmlFormatWriter)
{
XmlFormatWriter writer = (XmlFormatWriter)codedOutput;
if (options.FormattedOutput)
{
XmlWriterSettings settings = new XmlWriterSettings()
{
CheckCharacters = false,
NewLineHandling = NewLineHandling.Entitize,
OmitXmlDeclaration = true,
Encoding = new UTF8Encoding(false),
Indent = true,
IndentChars = " ",
NewLineChars = Environment.NewLine,
};
// Don't know how else to change xml writer options?
codedOutput = writer = XmlFormatWriter.CreateInstance(XmlWriter.Create(output, settings));
}
writer.RootElementName = options.XmlWriterRootElementName;
writer.Options = options.XmlWriterOptions;
}
return codedOutput;
}
/// <summary>
/// Writes the message instance to the stream using the content type provided
/// </summary>
/// <param name="message">An instance of a message</param>
/// <param name="options">Options specific to writing this message and/or content type</param>
/// <param name="contentType">The mime type of the content to be written</param>
/// <param name="output">The stream to write the message to</param>
public static void WriteTo(this IMessageLite message, MessageFormatOptions options, string contentType, Stream output)
{
ICodedOutputStream codedOutput = CreateOutputStream(options, contentType, output);
// Output the appropriate message preamble
codedOutput.WriteMessageStart();
// Write the message content to the output
message.WriteTo(codedOutput);
// Write the closing message fragment
codedOutput.WriteMessageEnd();
codedOutput.Flush();
}
private static ICodedInputStream ContentTypeToInputStream(string contentType, MessageFormatOptions options, Stream input)
{
contentType = (contentType ?? String.Empty).Split(';')[0].Trim();
Converter<Stream, ICodedInputStream> factory;
if(!options.MimeInputTypesReadOnly.TryGetValue(contentType, out factory) || factory == null)
{
if(String.IsNullOrEmpty(options.DefaultContentType) ||
!options.MimeInputTypesReadOnly.TryGetValue(options.DefaultContentType, out factory) || factory == null)
{
throw new ArgumentOutOfRangeException("contentType");
}
}
return factory(input);
}
private static ICodedOutputStream ContentTypeToOutputStream(string contentType, MessageFormatOptions options, Stream output)
{
contentType = (contentType ?? String.Empty).Split(';')[0].Trim();
Converter<Stream, ICodedOutputStream> factory;
if (!options.MimeOutputTypesReadOnly.TryGetValue(contentType, out factory) || factory == null)
{
if (String.IsNullOrEmpty(options.DefaultContentType) ||
!options.MimeOutputTypesReadOnly.TryGetValue(options.DefaultContentType, out factory) || factory == null)
{
throw new ArgumentOutOfRangeException("contentType");
}
}
return factory(output);
}
}
}
\ No newline at end of file
using System;
using System.IO;
using System.Collections.Generic;
using Google.ProtocolBuffers.Collections;
namespace Google.ProtocolBuffers.Serialization.Http
{
/// <summary>
/// Defines control information for the various formatting used with HTTP services
/// </summary>
public class MessageFormatOptions
{
/// <summary>The mime type for xml content</summary>
/// <remarks>Other valid xml mime types include: application/binary, application/x-protobuf</remarks>
public const string ContentTypeProtoBuffer = "application/vnd.google.protobuf";
/// <summary>The mime type for xml content</summary>
/// <remarks>Other valid xml mime types include: text/xml</remarks>
public const string ContentTypeXml = "application/xml";
/// <summary>The mime type for json content</summary>
/// <remarks>
/// Other valid json mime types include: application/json, application/x-json,
/// application/x-javascript, text/javascript, text/x-javascript, text/x-json, text/json
/// </remarks>
public const string ContentTypeJson = "application/json";
/// <summary>The mime type for query strings and x-www-form-urlencoded content</summary>
/// <remarks>This mime type is input-only</remarks>
public const string ContentFormUrlEncoded = "application/x-www-form-urlencoded";
/// <summary>
/// Default mime-type handling for input
/// </summary>
private static readonly IDictionary<string, Converter<Stream, ICodedInputStream>> MimeInputDefaults =
new ReadOnlyDictionary<string, Converter<Stream, ICodedInputStream>>(
new Dictionary<string, Converter<Stream, ICodedInputStream>>(StringComparer.OrdinalIgnoreCase)
{
{"application/json", JsonFormatReader.CreateInstance},
{"application/x-json", JsonFormatReader.CreateInstance},
{"application/x-javascript", JsonFormatReader.CreateInstance},
{"text/javascript", JsonFormatReader.CreateInstance},
{"text/x-javascript", JsonFormatReader.CreateInstance},
{"text/x-json", JsonFormatReader.CreateInstance},
{"text/json", JsonFormatReader.CreateInstance},
{"text/xml", XmlFormatReader.CreateInstance},
{"application/xml", XmlFormatReader.CreateInstance},
{"application/binary", CodedInputStream.CreateInstance},
{"application/x-protobuf", CodedInputStream.CreateInstance},
{"application/vnd.google.protobuf", CodedInputStream.CreateInstance},
{"application/x-www-form-urlencoded", FormUrlEncodedReader.CreateInstance},
}
);
/// <summary>
/// Default mime-type handling for output
/// </summary>
private static readonly IDictionary<string, Converter<Stream, ICodedOutputStream>> MimeOutputDefaults =
new ReadOnlyDictionary<string, Converter<Stream, ICodedOutputStream>>(
new Dictionary<string, Converter<Stream, ICodedOutputStream>>(StringComparer.OrdinalIgnoreCase)
{
{"application/json", JsonFormatWriter.CreateInstance},
{"application/x-json", JsonFormatWriter.CreateInstance},
{"application/x-javascript", JsonFormatWriter.CreateInstance},
{"text/javascript", JsonFormatWriter.CreateInstance},
{"text/x-javascript", JsonFormatWriter.CreateInstance},
{"text/x-json", JsonFormatWriter.CreateInstance},
{"text/json", JsonFormatWriter.CreateInstance},
{"text/xml", XmlFormatWriter.CreateInstance},
{"application/xml", XmlFormatWriter.CreateInstance},
{"application/binary", CodedOutputStream.CreateInstance},
{"application/x-protobuf", CodedOutputStream.CreateInstance},
{"application/vnd.google.protobuf", CodedOutputStream.CreateInstance},
}
);
private string _defaultContentType;
private string _xmlReaderRootElementName;
private string _xmlWriterRootElementName;
private ExtensionRegistry _extensionRegistry;
private Dictionary<string, Converter<Stream, ICodedInputStream>> _mimeInputTypes;
private Dictionary<string, Converter<Stream, ICodedOutputStream>> _mimeOutputTypes;
/// <summary> Provides access to modify the mime-type input stream construction </summary>
public IDictionary<string, Converter<Stream, ICodedInputStream>> MimeInputTypes
{
get
{
return _mimeInputTypes ??
(_mimeInputTypes = new Dictionary<string, Converter<Stream, ICodedInputStream>>(
MimeInputDefaults, StringComparer.OrdinalIgnoreCase));
}
}
/// <summary> Provides access to modify the mime-type input stream construction </summary>
public IDictionary<string, Converter<Stream, ICodedOutputStream>> MimeOutputTypes
{
get
{
return _mimeOutputTypes ??
(_mimeOutputTypes = new Dictionary<string, Converter<Stream, ICodedOutputStream>>(
MimeOutputDefaults, StringComparer.OrdinalIgnoreCase));
}
}
internal IDictionary<string, Converter<Stream, ICodedInputStream>> MimeInputTypesReadOnly
{ get { return _mimeInputTypes ?? MimeInputDefaults; } }
internal IDictionary<string, Converter<Stream, ICodedOutputStream>> MimeOutputTypesReadOnly
{ get { return _mimeOutputTypes ?? MimeOutputDefaults; } }
/// <summary>
/// The default content type to use if the input type is null or empty. If this
/// value is not supplied an ArgumentOutOfRangeException exception will be raised.
/// </summary>
public string DefaultContentType
{
get { return _defaultContentType ?? String.Empty; }
set { _defaultContentType = value; }
}
/// <summary>
/// The extension registry to use when reading messages
/// </summary>
public ExtensionRegistry ExtensionRegistry
{
get { return _extensionRegistry ?? ExtensionRegistry.Empty; }
set { _extensionRegistry = value; }
}
/// <summary>
/// The name of the xml root element when reading messages
/// </summary>
public string XmlReaderRootElementName
{
get { return _xmlReaderRootElementName ?? XmlFormatReader.DefaultRootElementName; }
set { _xmlReaderRootElementName = value; }
}
/// <summary>
/// Xml reader options
/// </summary>
public XmlReaderOptions XmlReaderOptions { get; set; }
/// <summary>
/// True to use formatted output including new-lines and default indentation
/// </summary>
public bool FormattedOutput { get; set; }
/// <summary>
/// The name of the xml root element when writing messages
/// </summary>
public string XmlWriterRootElementName
{
get { return _xmlWriterRootElementName ?? XmlFormatWriter.DefaultRootElementName; }
set { _xmlWriterRootElementName = value; }
}
/// <summary>
/// Xml writer options
/// </summary>
public XmlWriterOptions XmlWriterOptions { get; set; }
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers;
using System.IO;
namespace Google.ProtocolBuffers.Serialization.Http
{
/// <summary>
/// Extensions for the IRpcServerStub
/// </summary>
public static class ServiceExtensions
{
/// <summary>
/// Used to implement a service endpoint on an HTTP server. This works with services generated with the
/// service_generator_type option set to IRPCDISPATCH.
/// </summary>
/// <param name="stub">The service execution stub</param>
/// <param name="methodName">The name of the method being invoked</param>
/// <param name="options">optional arguments for the format reader/writer</param>
/// <param name="contentType">The mime type for the input stream</param>
/// <param name="input">The input stream</param>
/// <param name="responseType">The mime type for the output stream</param>
/// <param name="output">The output stream</param>
public static void HttpCallMethod(this IRpcServerStub stub, string methodName, MessageFormatOptions options,
string contentType, Stream input, string responseType, Stream output)
{
ICodedInputStream codedInput = MessageFormatFactory.CreateInputStream(options, contentType, input);
codedInput.ReadMessageStart();
IMessageLite response = stub.CallMethod(methodName, codedInput, options.ExtensionRegistry);
codedInput.ReadMessageEnd();
response.WriteTo(options, responseType, output);
}
}
}
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
......@@ -11,6 +11,7 @@ namespace Google.ProtocolBuffers.Serialization
public class JsonFormatReader : AbstractTextReader
{
private readonly JsonCursor _input;
// The expected token that ends the current item, either ']' or '}'
private readonly Stack<int> _stopChar;
private enum ReaderState
......@@ -101,17 +102,33 @@ namespace Google.ProtocolBuffers.Serialization
}
/// <summary>
/// Merges the contents of stream into the provided message builder
/// Reads the root-message preamble specific to this formatter
/// </summary>
public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
public override void ReadMessageStart()
{
_input.Consume('{');
_stopChar.Push('}');
_state = ReaderState.BeginObject;
builder.WeakMergeFrom(this, registry);
_input.Consume((char) _stopChar.Pop());
}
/// <summary>
/// Reads the root-message close specific to this formatter
/// </summary>
public override void ReadMessageEnd()
{
_input.Consume((char)_stopChar.Pop());
_state = ReaderState.EndValue;
}
/// <summary>
/// Merges the contents of stream into the provided message builder
/// </summary>
public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
{
ReadMessageStart();
builder.WeakMergeFrom(this, registry);
ReadMessageEnd();
return builder;
}
......
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
......@@ -101,7 +101,7 @@ namespace Google.ProtocolBuffers.Serialization
private class JsonStreamWriter : JsonFormatWriter
{
#if SILVERLIGHT2 || COMPACT_FRAMEWORK_35
static readonly Encoding Encoding = Encoding.UTF8;
static readonly Encoding Encoding = new UTF8Encoding(false);
#else
private static readonly Encoding Encoding = Encoding.ASCII;
#endif
......@@ -168,7 +168,9 @@ namespace Google.ProtocolBuffers.Serialization
#endregion
//Tracks the writer depth and the array element count at that depth.
private readonly List<int> _counter;
//True if the top-level of the writer is an array as opposed to a single message.
private bool _isArray;
/// <summary>
......@@ -243,7 +245,7 @@ namespace Google.ProtocolBuffers.Serialization
{
if (_counter.Count == 0)
{
throw new InvalidOperationException("Missmatched open/close in Json writer.");
throw new InvalidOperationException("Mismatched open/close in Json writer.");
}
int index = _counter.Count - 1;
......@@ -444,6 +446,18 @@ namespace Google.ProtocolBuffers.Serialization
/// Writes the message to the the formatted stream.
/// </summary>
public override void WriteMessage(IMessageLite message)
{
WriteMessageStart();
message.WriteTo(this);
WriteMessageEnd();
}
/// <summary>
/// Used to write the root-message preamble, in json this is the left-curly brace '{'.
/// After this call you can call IMessageLite.MergeTo(...) and complete the message with
/// a call to WriteMessageEnd().
/// </summary>
public override void WriteMessageStart()
{
if (_isArray)
{
......@@ -451,7 +465,13 @@ namespace Google.ProtocolBuffers.Serialization
}
WriteToOutput("{");
_counter.Add(0);
message.WriteTo(this);
}
/// <summary>
/// Used to complete a root-message previously started with a call to WriteMessageStart()
/// </summary>
public override void WriteMessageEnd()
{
_counter.RemoveAt(_counter.Count - 1);
WriteLine("}");
Flush();
......
......@@ -98,6 +98,10 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions.cs" />
<Compile Include="Http\FormUrlEncodedReader.cs" />
<Compile Include="Http\MessageFormatFactory.cs" />
<Compile Include="Http\MessageFormatOptions.cs" />
<Compile Include="Http\ServiceExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="AbstractReader.cs" />
<Compile Include="AbstractTextReader.cs" />
......
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Diagnostics;
namespace Google.ProtocolBuffers.Serialization
{
......@@ -14,8 +15,25 @@ namespace Google.ProtocolBuffers.Serialization
{
public const string DefaultRootElementName = XmlFormatWriter.DefaultRootElementName;
private readonly XmlReader _input;
// Tracks the message element for each nested message read
private readonly Stack<ElementStackEntry> _elements;
// The default element name for ReadMessageStart
private string _rootElementName;
private struct ElementStackEntry
{
public readonly string LocalName;
public readonly int Depth;
public readonly bool IsEmpty;
public ElementStackEntry(string localName, int depth, bool isEmpty) : this()
{
LocalName = localName;
IsEmpty = isEmpty;
Depth = depth;
}
}
private static XmlReaderSettings DefaultSettings
{
get
......@@ -72,20 +90,10 @@ namespace Google.ProtocolBuffers.Serialization
{
_input = input;
_rootElementName = DefaultRootElementName;
_elements = new Stack<ElementStackEntry>();
Options = XmlReaderOptions.None;
}
/// <summary>
/// Constructs the XmlFormatReader with the XmlReader and options
/// </summary>
protected XmlFormatReader(XmlFormatReader copyFrom, XmlReader input)
: base(copyFrom)
{
_input = input;
_rootElementName = copyFrom._rootElementName;
Options = copyFrom.Options;
}
/// <summary>
/// Gets or sets the options to use when reading the xml
/// </summary>
......@@ -113,26 +121,61 @@ namespace Google.ProtocolBuffers.Serialization
}
}
private XmlFormatReader CloneWith(XmlReader rdr)
[DebuggerNonUserCode]
private static void Assert(bool cond)
{
if (!cond)
{
throw new FormatException();
}
}
/// <summary>
/// Reads the root-message preamble specific to this formatter
/// </summary>
public override void ReadMessageStart()
{
XmlFormatReader copy = new XmlFormatReader(this, rdr);
return copy;
ReadMessageStart(_rootElementName);
}
private void NextElement()
/// <summary>
/// Reads the root-message preamble specific to this formatter
/// </summary>
public void ReadMessageStart(string element)
{
while (!_input.IsStartElement() && _input.Read())
{
continue;
}
Assert(_input.IsStartElement() && _input.LocalName == element);
_elements.Push(new ElementStackEntry(element, _input.Depth, _input.IsEmptyElement));
_input.Read();
}
private static void Assert(bool cond)
/// <summary>
/// Reads the root-message close specific to this formatter, MUST be called
/// on the reader obtained from ReadMessageStart(string element).
/// </summary>
public override void ReadMessageEnd()
{
if (!cond)
Assert(_elements.Count > 0);
ElementStackEntry stop = _elements.Peek();
while (_input.NodeType != XmlNodeType.EndElement && _input.NodeType != XmlNodeType.Element
&& _input.Depth > stop.Depth && _input.Read())
{
throw new FormatException();
continue;
}
if (!stop.IsEmpty)
{
Assert(_input.NodeType == XmlNodeType.EndElement
&& _input.LocalName == stop.LocalName
&& _input.Depth == stop.Depth);
_input.Read();
}
_elements.Pop();
}
/// <summary>
......@@ -157,9 +200,9 @@ namespace Google.ProtocolBuffers.Serialization
public TBuilder Merge<TBuilder>(string element, TBuilder builder, ExtensionRegistry registry)
where TBuilder : IBuilderLite
{
string field;
Assert(PeekNext(out field) && field == element);
ReadMessage(builder, registry);
ReadMessageStart(element);
builder.WeakMergeFrom(this, registry);
ReadMessageEnd();
return builder;
}
......@@ -172,7 +215,21 @@ namespace Google.ProtocolBuffers.Serialization
/// </remarks>
protected override bool PeekNext(out string field)
{
NextElement();
ElementStackEntry stopNode;
if (_elements.Count == 0)
{
stopNode = new ElementStackEntry(null, _input.Depth - 1, false);
}
else
{
stopNode = _elements.Peek();
}
while (!_input.IsStartElement() && _input.Depth > stopNode.Depth && _input.Read())
{
continue;
}
if (_input.IsStartElement())
{
field = _input.LocalName;
......@@ -235,20 +292,9 @@ namespace Google.ProtocolBuffers.Serialization
protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
{
Assert(_input.IsStartElement());
if (!_input.IsEmptyElement)
{
int depth = _input.Depth;
XmlReader child = _input.ReadSubtree();
while (!child.IsStartElement() && child.Read())
{
continue;
}
child.Read();
builder.WeakMergeFrom(CloneWith(child), registry);
Assert(depth == _input.Depth && _input.NodeType == XmlNodeType.EndElement);
}
_input.Read();
ReadMessageStart(_input.LocalName);
builder.WeakMergeFrom(this, registry);
ReadMessageEnd();
return true;
}
......@@ -270,27 +316,20 @@ namespace Google.ProtocolBuffers.Serialization
{
yield return item;
}
yield break;
}
if (!_input.IsEmptyElement)
else
{
int depth = _input.Depth;
XmlReader child = _input.ReadSubtree();
while (!child.IsStartElement() && child.Read())
string found;
ReadMessageStart(field);
if (PeekNext(out found) && found == "item")
{
continue;
}
child.Read();
foreach (string item in CloneWith(child).NonNestedArrayItems("item"))
foreach (string item in NonNestedArrayItems("item"))
{
yield return item;
}
Assert(depth == _input.Depth && _input.NodeType == XmlNodeType.EndElement);
}
_input.Read();
yield break;
ReadMessageEnd();
}
}
}
}
\ No newline at end of file
using System;
using System;
using System.Collections;
using System.IO;
using System.Text;
......@@ -14,10 +14,14 @@ namespace Google.ProtocolBuffers.Serialization
/// </summary>
public class XmlFormatWriter : AbstractTextWriter
{
private static readonly Encoding DefaultEncoding = new UTF8Encoding(false);
public const string DefaultRootElementName = "root";
private const int NestedArrayFlag = 0x0001;
private readonly XmlWriter _output;
// The default element name used for WriteMessageStart
private string _rootElementName;
// Used to assert matching WriteMessageStart/WriteMessageEnd calls
private int _messageOpenCount;
private static XmlWriterSettings DefaultSettings(Encoding encoding)
{
......@@ -43,7 +47,7 @@ namespace Google.ProtocolBuffers.Serialization
/// </summary>
public static XmlFormatWriter CreateInstance(Stream output)
{
return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(Encoding.UTF8)));
return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(DefaultEncoding)));
}
/// <summary>
......@@ -65,20 +69,10 @@ namespace Google.ProtocolBuffers.Serialization
protected XmlFormatWriter(XmlWriter output)
{
_output = output;
_messageOpenCount = 0;
_rootElementName = DefaultRootElementName;
}
/// <summary>
/// Closes the underlying XmlTextWriter
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
_output.Close();
}
}
/// <summary>
/// Gets or sets the default element name to use when using the Merge&lt;TBuilder>()
/// </summary>
......@@ -112,17 +106,30 @@ namespace Google.ProtocolBuffers.Serialization
}
/// <summary>
/// Writes a message as an element using the name defined in <see cref="RootElementName"/>
/// Completes any pending write operations
/// </summary>
public override void WriteMessage(IMessageLite message)
public override void Flush()
{
WriteMessage(_rootElementName, message);
_output.Flush();
base.Flush();
}
/// <summary>
/// Writes a message as an element with the given name
/// Used to write the root-message preamble, in xml this is open element for RootElementName,
/// by default "&lt;root&gt;". After this call you can call IMessageLite.MergeTo(...) and
/// complete the message with a call to WriteMessageEnd().
/// </summary>
public void WriteMessage(string elementName, IMessageLite message)
public override void WriteMessageStart()
{
WriteMessageStart(_rootElementName);
}
/// <summary>
/// Used to write the root-message preamble, in xml this is open element for elementName.
/// After this call you can call IMessageLite.MergeTo(...) and complete the message with
/// a call to WriteMessageEnd().
/// </summary>
public void WriteMessageStart(string elementName)
{
if (TestOption(XmlWriterOptions.OutputJsonTypes))
{
......@@ -133,10 +140,40 @@ namespace Google.ProtocolBuffers.Serialization
{
_output.WriteStartElement(elementName);
}
_messageOpenCount++;
}
/// <summary>
/// Used to complete a root-message previously started with a call to WriteMessageStart()
/// </summary>
public override void WriteMessageEnd()
{
if (_messageOpenCount <= 0)
{
throw new InvalidOperationException();
}
message.WriteTo(this);
_output.WriteEndElement();
_output.Flush();
_messageOpenCount--;
}
/// <summary>
/// Writes a message as an element using the name defined in <see cref="RootElementName"/>
/// </summary>
public override void WriteMessage(IMessageLite message)
{
WriteMessage(_rootElementName, message);
}
/// <summary>
/// Writes a message as an element with the given name
/// </summary>
public void WriteMessage(string elementName, IMessageLite message)
{
WriteMessageStart(elementName);
message.WriteTo(this);
WriteMessageEnd();
}
/// <summary>
......
......@@ -7,6 +7,24 @@ namespace Google.ProtocolBuffers.Compatibility
{
[TestFixture]
public class JsonCompatibilityTests : CompatibilityTests
{
protected override object SerializeMessage<TMessage, TBuilder>(TMessage message)
{
StringWriter sw = new StringWriter();
JsonFormatWriter.CreateInstance(sw)
.WriteMessage(message);
return sw.ToString();
}
protected override TBuilder DeserializeMessage<TMessage, TBuilder>(object message, TBuilder builder, ExtensionRegistry registry)
{
JsonFormatReader.CreateInstance((string)message).Merge(builder);
return builder;
}
}
[TestFixture]
public class JsonCompatibilityFormattedTests : CompatibilityTests
{
protected override object SerializeMessage<TMessage, TBuilder>(TMessage message)
{
......
using System.IO;
using System.Xml;
using Google.ProtocolBuffers.Serialization;
using Google.ProtocolBuffers.TestProtos;
using NUnit.Framework;
......@@ -22,4 +23,24 @@ namespace Google.ProtocolBuffers.Compatibility
return reader.Merge("root", builder, registry);
}
}
[TestFixture]
public class XmlCompatibilityFormattedTests : CompatibilityTests
{
protected override object SerializeMessage<TMessage, TBuilder>(TMessage message)
{
StringWriter text = new StringWriter();
XmlWriter xwtr = XmlWriter.Create(text, new XmlWriterSettings { Indent = true, IndentChars = " " });
XmlFormatWriter writer = XmlFormatWriter.CreateInstance(xwtr).SetOptions(XmlWriterOptions.OutputNestedArrays);
writer.WriteMessage("root", message);
return text.ToString();
}
protected override TBuilder DeserializeMessage<TMessage, TBuilder>(object message, TBuilder builder, ExtensionRegistry registry)
{
XmlFormatReader reader = XmlFormatReader.CreateInstance((string)message).SetOptions(XmlReaderOptions.ReadNestedArrays);
return reader.Merge("root", builder, registry);
}
}
}
\ No newline at end of file
......@@ -12,7 +12,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ProtocolBuffers.Test")]
[assembly: AssemblyCopyright("Copyright © 2008")]
[assembly: AssemblyCopyright("Copyright 2008")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
......
......@@ -88,6 +88,8 @@
</Compile>
<Compile Include="Compatibility\TextCompatibilityTests.cs" />
<Compile Include="Compatibility\XmlCompatibilityTests.cs" />
<Compile Include="TestRpcForMimeTypes.cs" />
<Compile Include="TestReaderForUrlEncoded.cs" />
<Compile Include="CSharpOptionsTest.cs" />
<Compile Include="DeprecatedMemberTest.cs" />
<Compile Include="DescriptorsTest.cs" />
......@@ -107,6 +109,7 @@
<Compile Include="SerializableTest.cs" />
<Compile Include="ServiceTest.cs" />
<Compile Include="TestCornerCases.cs" />
<Compile Include="TestMimeMessageFormats.cs" />
<Compile Include="TestProtos\UnitTestCSharpOptionsProtoFile.cs" />
<Compile Include="TestProtos\UnitTestCustomOptionsProtoFile.cs" />
<Compile Include="TestProtos\UnitTestEmbedOptimizeForProtoFile.cs" />
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Google.ProtocolBuffers.Serialization;
using Google.ProtocolBuffers.Serialization.Http;
using Google.ProtocolBuffers.TestProtos;
using NUnit.Framework;
namespace Google.ProtocolBuffers
{
[TestFixture]
public class TestMimeMessageFormats
{
// There is a whole host of various json mime types in use around the net, this is the set we accept...
readonly IEnumerable<string> JsonTypes = new string[] { "application/json", "application/x-json", "application/x-javascript", "text/javascript", "text/x-javascript", "text/x-json", "text/json" };
readonly IEnumerable<string> XmlTypes = new string[] { "text/xml", "application/xml" };
readonly IEnumerable<string> ProtobufTypes = new string[] { "application/binary", "application/x-protobuf", "application/vnd.google.protobuf" };
[Test]
public void TestReadJsonMimeTypes()
{
foreach (string type in JsonTypes)
{
Assert.IsTrue(
MessageFormatFactory.CreateInputStream(new MessageFormatOptions(), type, Stream.Null)
is JsonFormatReader);
}
Assert.IsTrue(
MessageFormatFactory.CreateInputStream(new MessageFormatOptions() { DefaultContentType = "application/json" }, null, Stream.Null)
is JsonFormatReader);
}
[Test]
public void TestWriteJsonMimeTypes()
{
foreach (string type in JsonTypes)
{
Assert.IsTrue(
MessageFormatFactory.CreateOutputStream(new MessageFormatOptions(), type, Stream.Null)
is JsonFormatWriter);
}
Assert.IsTrue(
MessageFormatFactory.CreateOutputStream(new MessageFormatOptions() { DefaultContentType = "application/json" }, null, Stream.Null)
is JsonFormatWriter);
}
[Test]
public void TestReadXmlMimeTypes()
{
foreach (string type in XmlTypes)
{
Assert.IsTrue(
MessageFormatFactory.CreateInputStream(new MessageFormatOptions(), type, Stream.Null)
is XmlFormatReader);
}
Assert.IsTrue(
MessageFormatFactory.CreateInputStream(new MessageFormatOptions() { DefaultContentType = "application/xml" }, null, Stream.Null)
is XmlFormatReader);
}
[Test]
public void TestWriteXmlMimeTypes()
{
foreach (string type in XmlTypes)
{
Assert.IsTrue(
MessageFormatFactory.CreateOutputStream(new MessageFormatOptions(), type, Stream.Null)
is XmlFormatWriter);
}
Assert.IsTrue(
MessageFormatFactory.CreateOutputStream(new MessageFormatOptions() { DefaultContentType = "application/xml" }, null, Stream.Null)
is XmlFormatWriter);
}
[Test]
public void TestReadProtoMimeTypes()
{
foreach (string type in ProtobufTypes)
{
Assert.IsTrue(
MessageFormatFactory.CreateInputStream(new MessageFormatOptions(), type, Stream.Null)
is CodedInputStream);
}
Assert.IsTrue(
MessageFormatFactory.CreateInputStream(new MessageFormatOptions() { DefaultContentType = "application/vnd.google.protobuf" }, null, Stream.Null)
is CodedInputStream);
}
[Test]
public void TestWriteProtoMimeTypes()
{
foreach (string type in ProtobufTypes)
{
Assert.IsTrue(
MessageFormatFactory.CreateOutputStream(new MessageFormatOptions(), type, Stream.Null)
is CodedOutputStream);
}
Assert.IsTrue(
MessageFormatFactory.CreateOutputStream(new MessageFormatOptions() { DefaultContentType = "application/vnd.google.protobuf" }, null, Stream.Null)
is CodedOutputStream);
}
[Test]
public void TestMergeFromJsonType()
{
TestXmlMessage msg = new TestXmlMessage.Builder().MergeFrom(
new MessageFormatOptions(), "application/json", new MemoryStream(Encoding.ASCII.GetBytes(
TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build().ToJson()
)))
.Build();
Assert.AreEqual("a", msg.Text);
Assert.AreEqual(1, msg.Number);
}
[Test]
public void TestMergeFromXmlType()
{
TestXmlMessage msg = new TestXmlMessage.Builder().MergeFrom(
new MessageFormatOptions(), "application/xml", new MemoryStream(Encoding.ASCII.GetBytes(
TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build().ToXml()
)))
.Build();
Assert.AreEqual("a", msg.Text);
Assert.AreEqual(1, msg.Number);
}
[Test]
public void TestMergeFromProtoType()
{
TestXmlMessage msg = new TestXmlMessage.Builder().MergeFrom(
new MessageFormatOptions(), "application/vnd.google.protobuf", new MemoryStream(
TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build().ToByteArray()
))
.Build();
Assert.AreEqual("a", msg.Text);
Assert.AreEqual(1, msg.Number);
}
[Test]
public void TestWriteToJsonType()
{
MemoryStream ms = new MemoryStream();
TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build()
.WriteTo(new MessageFormatOptions(), "application/json", ms);
Assert.AreEqual(@"{""text"":""a"",""number"":1}", Encoding.UTF8.GetString(ms.ToArray()));
}
[Test]
public void TestWriteToXmlType()
{
MemoryStream ms = new MemoryStream();
TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build()
.WriteTo(new MessageFormatOptions(), "application/xml", ms);
Assert.AreEqual("<root><text>a</text><number>1</number></root>", Encoding.UTF8.GetString(ms.ToArray()));
}
[Test]
public void TestWriteToProtoType()
{
MemoryStream ms = new MemoryStream();
TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build()
.WriteTo(new MessageFormatOptions(), "application/vnd.google.protobuf", ms);
byte[] bytes = TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build().ToByteArray();
Assert.AreEqual(bytes, ms.ToArray());
}
[Test]
public void TestXmlReaderOptions()
{
MemoryStream ms = new MemoryStream();
XmlFormatWriter.CreateInstance(ms)
.SetOptions(XmlWriterOptions.OutputNestedArrays)
.WriteMessage("my-root-node", TestXmlMessage.CreateBuilder().SetText("a").AddNumbers(1).AddNumbers(2).Build());
ms.Position = 0;
MessageFormatOptions options = new MessageFormatOptions()
{
XmlReaderOptions = XmlReaderOptions.ReadNestedArrays,
XmlReaderRootElementName = "my-root-node"
};
TestXmlMessage msg = new TestXmlMessage.Builder().MergeFrom(
options, "application/xml", ms)
.Build();
Assert.AreEqual("a", msg.Text);
Assert.AreEqual(1, msg.NumbersList[0]);
Assert.AreEqual(2, msg.NumbersList[1]);
}
[Test]
public void TestXmlWriterOptions()
{
TestXmlMessage message = TestXmlMessage.CreateBuilder().SetText("a").AddNumbers(1).AddNumbers(2).Build();
MessageFormatOptions options = new MessageFormatOptions()
{
XmlWriterOptions = XmlWriterOptions.OutputNestedArrays,
XmlWriterRootElementName = "root-node"
};
MemoryStream ms = new MemoryStream();
message.WriteTo(options, "application/xml", ms);
ms.Position = 0;
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
XmlFormatReader.CreateInstance(ms)
.SetOptions(XmlReaderOptions.ReadNestedArrays)
.Merge("root-node", builder);
Assert.AreEqual("a", builder.Text);
Assert.AreEqual(1, builder.NumbersList[0]);
Assert.AreEqual(2, builder.NumbersList[1]);
}
[Test]
public void TestJsonFormatted()
{
MemoryStream ms = new MemoryStream();
TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build()
.WriteTo(new MessageFormatOptions() { FormattedOutput = true }, "application/json", ms);
Assert.AreEqual("{\r\n \"text\": \"a\",\r\n \"number\": 1\r\n}", Encoding.UTF8.GetString(ms.ToArray()));
}
[Test]
public void TestXmlFormatted()
{
MemoryStream ms = new MemoryStream();
TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build()
.WriteTo(new MessageFormatOptions() { FormattedOutput = true }, "application/xml", ms);
Assert.AreEqual("<root>\r\n <text>a</text>\r\n <number>1</number>\r\n</root>", Encoding.UTF8.GetString(ms.ToArray()));
}
[Test]
public void TestReadCustomMimeTypes()
{
var options = new MessageFormatOptions();
//Remove existing mime-type mappings
options.MimeInputTypes.Clear();
//Add our own
options.MimeInputTypes.Add("-custom-XML-mime-type-", XmlFormatReader.CreateInstance);
Assert.AreEqual(1, options.MimeInputTypes.Count);
Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(
TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build().ToXml()
));
TestXmlMessage msg = new TestXmlMessage.Builder().MergeFrom(
options, "-custom-XML-mime-type-", xmlStream)
.Build();
Assert.AreEqual("a", msg.Text);
Assert.AreEqual(1, msg.Number);
}
[Test]
public void TestWriteToCustomType()
{
var options = new MessageFormatOptions();
//Remove existing mime-type mappings
options.MimeOutputTypes.Clear();
//Add our own
options.MimeOutputTypes.Add("-custom-XML-mime-type-", XmlFormatWriter.CreateInstance);
Assert.AreEqual(1, options.MimeOutputTypes.Count);
MemoryStream ms = new MemoryStream();
TestXmlMessage.CreateBuilder().SetText("a").SetNumber(1).Build()
.WriteTo(options, "-custom-XML-mime-type-", ms);
Assert.AreEqual("<root><text>a</text><number>1</number></root>", Encoding.UTF8.GetString(ms.ToArray()));
}
}
}
\ No newline at end of file
using System;
using System.IO;
using System.Text;
using NUnit.Framework;
using Google.ProtocolBuffers.TestProtos;
using Google.ProtocolBuffers.Serialization.Http;
namespace Google.ProtocolBuffers
{
[TestFixture]
public class TestReaderForUrlEncoded
{
[Test]
public void Example_FromQueryString()
{
Uri sampleUri = new Uri("http://sample.com/Path/File.ext?text=two+three%20four&valid=true&numbers=1&numbers=2", UriKind.Absolute);
ICodedInputStream input = FormUrlEncodedReader.CreateInstance(sampleUri.Query);
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
builder.MergeFrom(input);
TestXmlMessage message = builder.Build();
Assert.AreEqual(true, message.Valid);
Assert.AreEqual("two three four", message.Text);
Assert.AreEqual(2, message.NumbersCount);
Assert.AreEqual(1, message.NumbersList[0]);
Assert.AreEqual(2, message.NumbersList[1]);
}
[Test]
public void Example_FromFormData()
{
Stream rawPost = new MemoryStream(Encoding.UTF8.GetBytes("text=two+three%20four&valid=true&numbers=1&numbers=2"), false);
ICodedInputStream input = FormUrlEncodedReader.CreateInstance(rawPost);
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
builder.MergeFrom(input);
TestXmlMessage message = builder.Build();
Assert.AreEqual(true, message.Valid);
Assert.AreEqual("two three four", message.Text);
Assert.AreEqual(2, message.NumbersCount);
Assert.AreEqual(1, message.NumbersList[0]);
Assert.AreEqual(2, message.NumbersList[1]);
}
[Test]
public void TestEmptyValues()
{
ICodedInputStream input = FormUrlEncodedReader.CreateInstance("valid=true&text=&numbers=1");
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
builder.MergeFrom(input);
Assert.IsTrue(builder.Valid);
Assert.IsTrue(builder.HasText);
Assert.AreEqual("", builder.Text);
Assert.AreEqual(1, builder.NumbersCount);
Assert.AreEqual(1, builder.NumbersList[0]);
}
[Test]
public void TestNoValue()
{
ICodedInputStream input = FormUrlEncodedReader.CreateInstance("valid=true&text&numbers=1");
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
builder.MergeFrom(input);
Assert.IsTrue(builder.Valid);
Assert.IsTrue(builder.HasText);
Assert.AreEqual("", builder.Text);
Assert.AreEqual(1, builder.NumbersCount);
Assert.AreEqual(1, builder.NumbersList[0]);
}
[Test, ExpectedException(typeof(NotSupportedException))]
public void FormUrlEncodedReaderDoesNotSupportChildren()
{
ICodedInputStream input = FormUrlEncodedReader.CreateInstance("child=uh0");
TestXmlMessage.CreateBuilder().MergeFrom(input);
}
}
}
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://github.com/jskeet/dotnet-protobufs/
// Original C++/Java/Python code:
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using Google.ProtocolBuffers;
using Google.ProtocolBuffers.Serialization.Http;
using Google.ProtocolBuffers.TestProtos;
using NUnit.Framework;
using System.IO;
using Google.ProtocolBuffers.Serialization;
using System.Text;
namespace Google.ProtocolBuffers
{
/// <summary>
/// This class verifies the correct code is generated from unittest_rpc_interop.proto and provides a small demonstration
/// of using the new IRpcDispatch to write a client/server
/// </summary>
[TestFixture]
public class TestRpcForMimeTypes
{
/// <summary>
/// A sample implementation of the ISearchService for testing
/// </summary>
private class ExampleSearchImpl : ISearchService
{
SearchResponse ISearchService.Search(SearchRequest searchRequest)
{
if (searchRequest.CriteriaCount == 0)
{
throw new ArgumentException("No criteria specified.", new InvalidOperationException());
}
SearchResponse.Builder resp = SearchResponse.CreateBuilder();
foreach (string criteria in searchRequest.CriteriaList)
{
resp.AddResults(
SearchResponse.Types.ResultItem.CreateBuilder().SetName(criteria).SetUrl("http://search.com").
Build());
}
return resp.Build();
}
SearchResponse ISearchService.RefineSearch(RefineSearchRequest refineSearchRequest)
{
SearchResponse.Builder resp = refineSearchRequest.PreviousResults.ToBuilder();
foreach (string criteria in refineSearchRequest.CriteriaList)
{
resp.AddResults(
SearchResponse.Types.ResultItem.CreateBuilder().SetName(criteria).SetUrl("http://refine.com").
Build());
}
return resp.Build();
}
}
/// <summary>
/// An example extraction of the wire protocol
/// </summary>
private interface IHttpTransfer
{
void Execute(string method, string contentType, Stream input, string acceptType, Stream output);
}
/// <summary>
/// An example of a server responding to a web/http request
/// </summary>
private class ExampleHttpServer : IHttpTransfer
{
public readonly MessageFormatOptions Options =
new MessageFormatOptions
{
ExtensionRegistry = ExtensionRegistry.Empty,
FormattedOutput = true,
XmlReaderOptions = XmlReaderOptions.ReadNestedArrays,
XmlReaderRootElementName = "request",
XmlWriterOptions = XmlWriterOptions.OutputNestedArrays,
XmlWriterRootElementName = "response"
};
private readonly IRpcServerStub _stub;
public ExampleHttpServer(ISearchService implementation)
{
//on the server, we create a dispatch to call the appropriate method by name
IRpcDispatch dispatch = new SearchService.Dispatch(implementation);
//we then wrap that dispatch in a server stub which will deserialize the wire bytes to the message
//type appropriate for the method name being invoked.
_stub = new SearchService.ServerStub(dispatch);
}
void IHttpTransfer.Execute(string method, string contentType, Stream input, string acceptType, Stream output)
{
//Extension for: Google.ProtocolBuffers.Serialization.Http.ServiceExtensions.HttpCallMethod(_stub,
_stub.HttpCallMethod(
method, Options,
contentType, input,
acceptType, output
);
}
}
/// <summary>
/// An example of a client sending a wire request
/// </summary>
private class ExampleClient : IRpcDispatch
{
public readonly MessageFormatOptions Options =
new MessageFormatOptions
{
ExtensionRegistry = ExtensionRegistry.Empty,
FormattedOutput = true,
XmlReaderOptions = XmlReaderOptions.ReadNestedArrays,
XmlReaderRootElementName = "response",
XmlWriterOptions = XmlWriterOptions.OutputNestedArrays,
XmlWriterRootElementName = "request"
};
private readonly IHttpTransfer _wire;
private readonly string _mimeType;
public ExampleClient(IHttpTransfer wire, string mimeType)
{
_wire = wire;
_mimeType = mimeType;
}
TMessage IRpcDispatch.CallMethod<TMessage, TBuilder>(string method, IMessageLite request,
IBuilderLite<TMessage, TBuilder> response)
{
MemoryStream input = new MemoryStream();
MemoryStream output = new MemoryStream();
//Write to _mimeType format
request.WriteTo(Options, _mimeType, input);
input.Position = 0;
_wire.Execute(method, _mimeType, input, _mimeType, output);
//Read from _mimeType format
output.Position = 0;
response.MergeFrom(Options, _mimeType, output);
return response.Build();
}
}
/// <summary>
/// Test sending and recieving messages via text/json
/// </summary>
[Test]
public void TestClientServerWithJsonFormat()
{
ExampleHttpServer server = new ExampleHttpServer(new ExampleSearchImpl());
//obviously if this was a 'real' transport we would not use the server, rather the server would be listening, the client transmitting
IHttpTransfer wire = server;
ISearchService client = new SearchService(new ExampleClient(wire, "text/json"));
//now the client has a real, typed, interface to work with:
SearchResponse result = client.Search(SearchRequest.CreateBuilder().AddCriteria("Test").Build());
Assert.AreEqual(1, result.ResultsCount);
Assert.AreEqual("Test", result.ResultsList[0].Name);
Assert.AreEqual("http://search.com", result.ResultsList[0].Url);
//The test part of this, call the only other method
result =
client.RefineSearch(
RefineSearchRequest.CreateBuilder().SetPreviousResults(result).AddCriteria("Refine").Build());
Assert.AreEqual(2, result.ResultsCount);
Assert.AreEqual("Test", result.ResultsList[0].Name);
Assert.AreEqual("http://search.com", result.ResultsList[0].Url);
Assert.AreEqual("Refine", result.ResultsList[1].Name);
Assert.AreEqual("http://refine.com", result.ResultsList[1].Url);
}
/// <summary>
/// Test sending and recieving messages via text/json
/// </summary>
[Test]
public void TestClientServerWithXmlFormat()
{
ExampleHttpServer server = new ExampleHttpServer(new ExampleSearchImpl());
//obviously if this was a 'real' transport we would not use the server, rather the server would be listening, the client transmitting
IHttpTransfer wire = server;
ISearchService client = new SearchService(new ExampleClient(wire, "text/xml"));
//now the client has a real, typed, interface to work with:
SearchResponse result = client.Search(SearchRequest.CreateBuilder().AddCriteria("Test").Build());
Assert.AreEqual(1, result.ResultsCount);
Assert.AreEqual("Test", result.ResultsList[0].Name);
Assert.AreEqual("http://search.com", result.ResultsList[0].Url);
//The test part of this, call the only other method
result =
client.RefineSearch(
RefineSearchRequest.CreateBuilder().SetPreviousResults(result).AddCriteria("Refine").Build());
Assert.AreEqual(2, result.ResultsCount);
Assert.AreEqual("Test", result.ResultsList[0].Name);
Assert.AreEqual("http://search.com", result.ResultsList[0].Url);
Assert.AreEqual("Refine", result.ResultsList[1].Name);
Assert.AreEqual("http://refine.com", result.ResultsList[1].Url);
}
/// <summary>
/// Test sending and recieving messages via text/json
/// </summary>
[Test]
public void TestClientServerWithProtoFormat()
{
ExampleHttpServer server = new ExampleHttpServer(new ExampleSearchImpl());
//obviously if this was a 'real' transport we would not use the server, rather the server would be listening, the client transmitting
IHttpTransfer wire = server;
ISearchService client = new SearchService(new ExampleClient(wire, "application/x-protobuf"));
//now the client has a real, typed, interface to work with:
SearchResponse result = client.Search(SearchRequest.CreateBuilder().AddCriteria("Test").Build());
Assert.AreEqual(1, result.ResultsCount);
Assert.AreEqual("Test", result.ResultsList[0].Name);
Assert.AreEqual("http://search.com", result.ResultsList[0].Url);
//The test part of this, call the only other method
result =
client.RefineSearch(
RefineSearchRequest.CreateBuilder().SetPreviousResults(result).AddCriteria("Refine").Build());
Assert.AreEqual(2, result.ResultsCount);
Assert.AreEqual("Test", result.ResultsList[0].Name);
Assert.AreEqual("http://search.com", result.ResultsList[0].Url);
Assert.AreEqual("Refine", result.ResultsList[1].Name);
Assert.AreEqual("http://refine.com", result.ResultsList[1].Url);
}
/// <summary>
/// Test sending and recieving messages via text/json
/// </summary>
[Test]
public void TestClientServerWithCustomFormat()
{
ExampleHttpServer server = new ExampleHttpServer(new ExampleSearchImpl());
//Setup our custom mime-type format as the only format supported:
server.Options.MimeInputTypes.Clear();
server.Options.MimeInputTypes.Add("foo/bar", CodedInputStream.CreateInstance);
server.Options.MimeOutputTypes.Clear();
server.Options.MimeOutputTypes.Add("foo/bar", CodedOutputStream.CreateInstance);
//obviously if this was a 'real' transport we would not use the server, rather the server would be listening, the client transmitting
IHttpTransfer wire = server;
ExampleClient exclient = new ExampleClient(wire, "foo/bar");
//Add our custom mime-type format
exclient.Options.MimeInputTypes.Add("foo/bar", CodedInputStream.CreateInstance);
exclient.Options.MimeOutputTypes.Add("foo/bar", CodedOutputStream.CreateInstance);
ISearchService client = new SearchService(exclient);
//now the client has a real, typed, interface to work with:
SearchResponse result = client.Search(SearchRequest.CreateBuilder().AddCriteria("Test").Build());
Assert.AreEqual(1, result.ResultsCount);
Assert.AreEqual("Test", result.ResultsList[0].Name);
Assert.AreEqual("http://search.com", result.ResultsList[0].Url);
//The test part of this, call the only other method
result =
client.RefineSearch(
RefineSearchRequest.CreateBuilder().SetPreviousResults(result).AddCriteria("Refine").Build());
Assert.AreEqual(2, result.ResultsCount);
Assert.AreEqual("Test", result.ResultsList[0].Name);
Assert.AreEqual("http://search.com", result.ResultsList[0].Url);
Assert.AreEqual("Refine", result.ResultsList[1].Name);
Assert.AreEqual("http://refine.com", result.ResultsList[1].Url);
}
/// <summary>
/// Test sending and recieving messages via text/json
/// </summary>
[Test]
public void TestServerWithUriFormat()
{
ExampleHttpServer server = new ExampleHttpServer(new ExampleSearchImpl());
//obviously if this was a 'real' transport we would not use the server, rather the server would be listening, the client transmitting
IHttpTransfer wire = server;
MemoryStream input = new MemoryStream(Encoding.UTF8.GetBytes("?Criteria=Test&Criteria=Test+of%20URI"));
MemoryStream output = new MemoryStream();
//Call the server
wire.Execute("Search",
MessageFormatOptions.ContentFormUrlEncoded, input,
MessageFormatOptions.ContentTypeProtoBuffer, output
);
SearchResponse result = SearchResponse.ParseFrom(output.ToArray());
Assert.AreEqual(2, result.ResultsCount);
Assert.AreEqual("Test", result.ResultsList[0].Name);
Assert.AreEqual("http://search.com", result.ResultsList[0].Url);
Assert.AreEqual("Test of URI", result.ResultsList[1].Name);
Assert.AreEqual("http://search.com", result.ResultsList[1].Url);
}
/// <summary>
/// Test sending and recieving messages via text/json
/// </summary>
[Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
public void TestInvalidMimeType()
{
ExampleHttpServer server = new ExampleHttpServer(new ExampleSearchImpl());
//obviously if this was a 'real' transport we would not use the server, rather the server would be listening, the client transmitting
IHttpTransfer wire = server;
MemoryStream input = new MemoryStream();
MemoryStream output = new MemoryStream();
//Call the server
wire.Execute("Search",
"bad/mime", input,
MessageFormatOptions.ContentTypeProtoBuffer, output
);
Assert.Fail();
}
/// <summary>
/// Test sending and recieving messages via text/json
/// </summary>
[Test]
public void TestDefaultMimeType()
{
ExampleHttpServer server = new ExampleHttpServer(new ExampleSearchImpl());
//obviously if this was a 'real' transport we would not use the server, rather the server would be listening, the client transmitting
IHttpTransfer wire = server;
MemoryStream input = new MemoryStream(new SearchRequest.Builder().AddCriteria("Test").Build().ToByteArray());
MemoryStream output = new MemoryStream();
//With this default set, any invalid/unknown mime-type will be mapped to use that format
server.Options.DefaultContentType = MessageFormatOptions.ContentTypeProtoBuffer;
wire.Execute("Search",
"foo", input,
"bar", output
);
SearchResponse result = SearchResponse.ParseFrom(output.ToArray());
Assert.AreEqual(1, result.ResultsCount);
Assert.AreEqual("Test", result.ResultsList[0].Name);
Assert.AreEqual("http://search.com", result.ResultsList[0].Url);
}
}
}
\ No newline at end of file
......@@ -10,6 +10,70 @@ namespace Google.ProtocolBuffers
[TestFixture]
public class TestWriterFormatJson
{
[Test]
public void Example_FromJson()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
builder.MergeFromJson(@"{""valid"":true}");
TestXmlMessage message = builder.Build();
Assert.AreEqual(true, message.Valid);
}
[Test]
public void Example_ToJson()
{
TestXmlMessage message =
TestXmlMessage.CreateBuilder()
.SetValid(true)
.Build();
string json = message.ToJson();
Assert.AreEqual(@"{""valid"":true}", json);
}
[Test]
public void Example_WriteJsonUsingICodedOutputStream()
{
TestXmlMessage message =
TestXmlMessage.CreateBuilder()
.SetValid(true)
.Build();
using (TextWriter output = new StringWriter())
{
ICodedOutputStream writer = JsonFormatWriter.CreateInstance(output);
writer.WriteMessageStart(); //manually begin the message, output is '{'
writer.Flush();
Assert.AreEqual("{", output.ToString());
ICodedOutputStream stream = writer;
message.WriteTo(stream); //write the message normally
writer.Flush();
Assert.AreEqual(@"{""valid"":true", output.ToString());
writer.WriteMessageEnd(); //manually write the end message '}'
Assert.AreEqual(@"{""valid"":true}", output.ToString());
}
}
[Test]
public void Example_ReadJsonUsingICodedInputStream()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
ICodedInputStream reader = JsonFormatReader.CreateInstance(@"{""valid"":true}");
reader.ReadMessageStart(); //manually read the begin the message '{'
builder.MergeFrom(reader); //write the message normally
reader.ReadMessageEnd(); //manually read the end message '}'
}
protected string Content;
[System.Diagnostics.DebuggerNonUserCode]
protected void FormatterAssert<TMessage>(TMessage message, params string[] expecting) where TMessage : IMessageLite
......@@ -337,6 +401,28 @@ namespace Google.ProtocolBuffers
Assert.AreEqual(3, ordinal);
Assert.AreEqual(3, builder.TextlinesCount);
}
[Test]
public void TestReadWriteJsonWithoutRoot()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
TestXmlMessage message = builder.SetText("abc").SetNumber(123).Build();
string Json;
using (StringWriter sw = new StringWriter())
{
ICodedOutputStream output = JsonFormatWriter.CreateInstance(sw);
message.WriteTo(output);
output.Flush();
Json = sw.ToString();
}
Assert.AreEqual(@"""text"":""abc"",""number"":123", Json);
ICodedInputStream input = JsonFormatReader.CreateInstance(Json);
TestXmlMessage copy = TestXmlMessage.CreateBuilder().MergeFrom(input).Build();
Assert.AreEqual(message, copy);
}
[Test,ExpectedException(typeof(RecursionLimitExceededException))]
public void TestRecursiveLimit()
{
......
......@@ -12,6 +12,65 @@ namespace Google.ProtocolBuffers
[TestFixture]
public class TestWriterFormatXml
{
[Test]
public void Example_FromXml()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
XmlReader rdr = XmlReader.Create(new StringReader(@"<root><valid>true</valid></root>"));
builder.MergeFromXml(rdr);
TestXmlMessage message = builder.Build();
Assert.AreEqual(true, message.Valid);
}
[Test]
public void Example_ToXml()
{
TestXmlMessage message =
TestXmlMessage.CreateBuilder()
.SetValid(true)
.Build();
string Xml = message.ToXml();
Assert.AreEqual(@"<root><valid>true</valid></root>", Xml);
}
[Test]
public void Example_WriteXmlUsingICodedOutputStream()
{
TestXmlMessage message =
TestXmlMessage.CreateBuilder()
.SetValid(true)
.Build();
using (TextWriter output = new StringWriter())
{
ICodedOutputStream writer = XmlFormatWriter.CreateInstance(output);
writer.WriteMessageStart(); //manually begin the message, output is '{'
ICodedOutputStream stream = writer;
message.WriteTo(stream); //write the message normally
writer.WriteMessageEnd(); //manually write the end message '}'
Assert.AreEqual(@"<root><valid>true</valid></root>", output.ToString());
}
}
[Test]
public void Example_ReadXmlUsingICodedInputStream()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
ICodedInputStream reader = XmlFormatReader.CreateInstance(@"<root><valid>true</valid></root>");
reader.ReadMessageStart(); //manually read the begin the message '{'
builder.MergeFrom(reader); //read the message normally
reader.ReadMessageEnd(); //manually read the end message '}'
}
[Test]
public void TestToXmlParseFromXml()
{
......@@ -135,7 +194,9 @@ namespace Google.ProtocolBuffers
.Build();
StringWriter sw = new StringWriter();
XmlFormatWriter.CreateInstance(sw).WriteMessage("root", message);
XmlWriter xwtr = XmlWriter.Create(sw, new XmlWriterSettings {Indent = true, IndentChars = " "});
XmlFormatWriter.CreateInstance(xwtr).WriteMessage("root", message);
string xml = sw.ToString();
......@@ -162,7 +223,9 @@ namespace Google.ProtocolBuffers
.Build();
StringWriter sw = new StringWriter();
XmlFormatWriter.CreateInstance(sw)
XmlWriter xwtr = XmlWriter.Create(sw, new XmlWriterSettings { Indent = true, IndentChars = " " });
XmlFormatWriter.CreateInstance(xwtr)
.SetOptions(XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputEnumValues)
.WriteMessage("root", message);
......@@ -324,6 +387,60 @@ namespace Google.ProtocolBuffers
TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder(), registry).Build();
Assert.AreEqual(message, copy);
}
[Test]
public void TestXmlReadEmptyRoot()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
ICodedInputStream reader = XmlFormatReader.CreateInstance(@"<root/>");
reader.ReadMessageStart(); //manually read the begin the message '{'
builder.MergeFrom(reader); //write the message normally
reader.ReadMessageEnd(); //manually read the end message '}'
}
[Test]
public void TestXmlReadEmptyChild()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
ICodedInputStream reader = XmlFormatReader.CreateInstance(@"<root><text /></root>");
reader.ReadMessageStart(); //manually read the begin the message '{'
builder.MergeFrom(reader); //write the message normally
Assert.IsTrue(builder.HasText);
Assert.AreEqual(String.Empty, builder.Text);
}
[Test]
public void TestXmlReadWriteWithoutRoot()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
TestXmlMessage message = builder.SetText("abc").SetNumber(123).Build();
string xml;
using (StringWriter sw = new StringWriter())
{
ICodedOutputStream output = XmlFormatWriter.CreateInstance(
XmlWriter.Create(sw, new XmlWriterSettings() { ConformanceLevel = ConformanceLevel.Fragment }));
message.WriteTo(output);
output.Flush();
xml = sw.ToString();
}
Assert.AreEqual("<text>abc</text><number>123</number>", xml);
TestXmlMessage copy;
using (XmlReader xr = XmlReader.Create(new StringReader(xml), new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment }))
{
ICodedInputStream input = XmlFormatReader.CreateInstance(xr);
copy = TestXmlMessage.CreateBuilder().MergeFrom(input).Build();
}
Assert.AreEqual(message, copy);
}
[Test, ExpectedException(typeof(RecursionLimitExceededException))]
public void TestRecursiveLimit()
{
......
......@@ -144,6 +144,9 @@ namespace Google.ProtocolBuffers
#endregion
void ICodedInputStream.ReadMessageStart() { }
void ICodedInputStream.ReadMessageEnd() { }
#region Validation
/// <summary>
......
......@@ -126,6 +126,9 @@ namespace Google.ProtocolBuffers
#endregion
void ICodedOutputStream.WriteMessageStart() { }
void ICodedOutputStream.WriteMessageEnd() { Flush(); }
#region Writing of unknown fields
[Obsolete]
......
......@@ -45,6 +45,24 @@ namespace Google.ProtocolBuffers
{
public interface ICodedInputStream
{
/// <summary>
/// Reads any message initialization data expected from the input stream
/// </summary>
/// <remarks>
/// This is primarily used by text formats and unnecessary for protobuffers' own
/// binary format. The API for MessageStart/End was added for consistent handling
/// of output streams regardless of the actual writer implementation.
/// </remarks>
void ReadMessageStart();
/// <summary>
/// Reads any message finalization data expected from the input stream
/// </summary>
/// <remarks>
/// This is primarily used by text formats and unnecessary for protobuffers' own
/// binary format. The API for MessageStart/End was added for consistent handling
/// of output streams regardless of the actual writer implementation.
/// </remarks>
void ReadMessageEnd();
/// <summary>
/// Attempt to read a field tag, returning false if we have reached the end
/// of the input data.
......
......@@ -51,6 +51,24 @@ namespace Google.ProtocolBuffers
/// </summary>
public interface ICodedOutputStream
{
/// <summary>
/// Writes any message initialization data needed to the output stream
/// </summary>
/// <remarks>
/// This is primarily used by text formats and unnecessary for protobuffers' own
/// binary format. The API for MessageStart/End was added for consistent handling
/// of output streams regardless of the actual writer implementation.
/// </remarks>
void WriteMessageStart();
/// <summary>
/// Writes any message finalization data needed to the output stream
/// </summary>
/// <remarks>
/// This is primarily used by text formats and unnecessary for protobuffers' own
/// binary format. The API for MessageStart/End was added for consistent handling
/// of output streams regardless of the actual writer implementation.
/// </remarks>
void WriteMessageEnd();
/// <summary>
/// Indicates that all temporary buffers be written to the final output.
/// </summary>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment