aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcsharptest <roger@csharptest.net>2011-09-09 12:18:16 -0500
committerrogerk <devnull@localhost>2011-09-09 12:18:16 -0500
commit60fd773d300cd0390ba08812590690ab2d28c837 (patch)
tree292994e6c5602566ed50d9911522f2e5e80515b1
parent819b7154d162f3ef4f187f19b020f999c02fcf03 (diff)
downloadprotobuf-60fd773d300cd0390ba08812590690ab2d28c837.tar.gz
protobuf-60fd773d300cd0390ba08812590690ab2d28c837.tar.bz2
protobuf-60fd773d300cd0390ba08812590690ab2d28c837.zip
Completed work and testing for manually reading/writing start/end message
-rw-r--r--src/ProtocolBuffers.Serialization/AbstractReader.cs10
-rw-r--r--src/ProtocolBuffers.Serialization/AbstractTextReader.cs4
-rw-r--r--src/ProtocolBuffers.Serialization/AbstractWriter.cs12
-rw-r--r--src/ProtocolBuffers.Serialization/DictionaryReader.cs13
-rw-r--r--src/ProtocolBuffers.Serialization/DictionaryWriter.cs6
-rw-r--r--src/ProtocolBuffers.Serialization/Http/MessageFormatFactory.cs33
-rw-r--r--src/ProtocolBuffers.Serialization/Http/MessageFormatOptions.cs2
-rw-r--r--src/ProtocolBuffers.Serialization/Http/ServiceExtensions.cs3
-rw-r--r--src/ProtocolBuffers.Serialization/JsonFormatReader.cs13
-rw-r--r--src/ProtocolBuffers.Serialization/JsonFormatWriter.cs21
-rw-r--r--src/ProtocolBuffers.Serialization/XmlFormatReader.cs142
-rw-r--r--src/ProtocolBuffers.Serialization/XmlFormatWriter.cs28
-rw-r--r--src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj1
-rw-r--r--src/ProtocolBuffers.Test/TestMimeMessageFormats.cs225
-rw-r--r--src/ProtocolBuffers.Test/TestWriterFormatJson.cs34
-rw-r--r--src/ProtocolBuffers.Test/TestWriterFormatXml.cs63
-rw-r--r--src/ProtocolBuffers/CodedInputStream.cs3
-rw-r--r--src/ProtocolBuffers/CodedOutputStream.cs3
-rw-r--r--src/ProtocolBuffers/ICodedInputStream.cs18
-rw-r--r--src/ProtocolBuffers/ICodedOutputStream.cs18
20 files changed, 486 insertions, 166 deletions
diff --git a/src/ProtocolBuffers.Serialization/AbstractReader.cs b/src/ProtocolBuffers.Serialization/AbstractReader.cs
index 538af38f..f3e6fd6f 100644
--- a/src/ProtocolBuffers.Serialization/AbstractReader.cs
+++ b/src/ProtocolBuffers.Serialization/AbstractReader.cs
@@ -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; }
@@ -116,12 +110,12 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary>
/// Reads the root-message preamble specific to this formatter
/// </summary>
- public abstract AbstractReader ReadStartMessage();
+ public abstract void ReadMessageStart();
/// <summary>
/// Reads the root-message close specific to this formatter
/// </summary>
- public abstract void ReadEndMessage();
+ public abstract void ReadMessageEnd();
/// <summary>
/// Merges the input stream into the provided IBuilderLite
diff --git a/src/ProtocolBuffers.Serialization/AbstractTextReader.cs b/src/ProtocolBuffers.Serialization/AbstractTextReader.cs
index 59b9057b..ddfa6436 100644
--- a/src/ProtocolBuffers.Serialization/AbstractTextReader.cs
+++ b/src/ProtocolBuffers.Serialization/AbstractTextReader.cs
@@ -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
diff --git a/src/ProtocolBuffers.Serialization/AbstractWriter.cs b/src/ProtocolBuffers.Serialization/AbstractWriter.cs
index 50dfe671..7ae26864 100644
--- a/src/ProtocolBuffers.Serialization/AbstractWriter.cs
+++ b/src/ProtocolBuffers.Serialization/AbstractWriter.cs
@@ -45,23 +45,23 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary>
/// Used to write any nessary root-message preamble. After this call you can call
- /// IMessageLite.MergeTo(...) and complete the message with a call to EndMessage().
+ /// IMessageLite.MergeTo(...) and complete the message with a call to WriteMessageEnd().
/// These three calls are identical to just calling WriteMessage(message);
/// </summary>
/// <example>
/// AbstractWriter writer;
- /// writer.StartMessage();
+ /// writer.WriteMessageStart();
/// message.WriteTo(writer);
- /// writer.EndMessage();
+ /// writer.WriteMessageEnd();
/// // ... or, but not both ...
/// writer.WriteMessage(message);
/// </example>
- public abstract void StartMessage();
+ public abstract void WriteMessageStart();
/// <summary>
- /// Used to complete a root-message previously started with a call to StartMessage()
+ /// Used to complete a root-message previously started with a call to WriteMessageStart()
/// </summary>
- public abstract void EndMessage();
+ public abstract void WriteMessageEnd();
/// <summary>
/// Writes a Boolean value
diff --git a/src/ProtocolBuffers.Serialization/DictionaryReader.cs b/src/ProtocolBuffers.Serialization/DictionaryReader.cs
index f606bc9b..bb83ef9c 100644
--- a/src/ProtocolBuffers.Serialization/DictionaryReader.cs
+++ b/src/ProtocolBuffers.Serialization/DictionaryReader.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using Google.ProtocolBuffers.Descriptors;
@@ -25,17 +25,14 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary>
/// No-op
/// </summary>
- public override AbstractReader ReadStartMessage()
- {
- return this;
- }
+ public override void ReadMessageStart()
+ { }
/// <summary>
/// No-op
/// </summary>
- public override void ReadEndMessage()
- {
- }
+ public override void ReadMessageEnd()
+ { }
/// <summary>
/// Merges the contents of stream into the provided message builder
diff --git a/src/ProtocolBuffers.Serialization/DictionaryWriter.cs b/src/ProtocolBuffers.Serialization/DictionaryWriter.cs
index 4c3b0116..6d823301 100644
--- a/src/ProtocolBuffers.Serialization/DictionaryWriter.cs
+++ b/src/ProtocolBuffers.Serialization/DictionaryWriter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections;
using System.Collections.Generic;
using Google.ProtocolBuffers.Descriptors;
@@ -57,13 +57,13 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary>
/// No-op
/// </summary>
- public override void StartMessage()
+ public override void WriteMessageStart()
{ }
/// <summary>
/// No-op
/// </summary>
- public override void EndMessage()
+ public override void WriteMessageEnd()
{ }
/// <summary>
diff --git a/src/ProtocolBuffers.Serialization/Http/MessageFormatFactory.cs b/src/ProtocolBuffers.Serialization/Http/MessageFormatFactory.cs
index 8a19a8b7..2ee7eb4d 100644
--- a/src/ProtocolBuffers.Serialization/Http/MessageFormatFactory.cs
+++ b/src/ProtocolBuffers.Serialization/Http/MessageFormatFactory.cs
@@ -3,7 +3,7 @@ using System.IO;
using System.Xml;
using System.Text;
-namespace Google.ProtocolBuffers.Serialization
+namespace Google.ProtocolBuffers.Serialization.Http
{
/// <summary>
/// Extensions and helpers to abstract the reading/writing of messages by a client-specified content type.
@@ -29,14 +29,14 @@ namespace Google.ProtocolBuffers.Serialization
else if (inputType == FormatType.Json)
{
JsonFormatReader reader = JsonFormatReader.CreateInstance(input);
- codedInput = reader.ReadStartMessage();
+ codedInput = reader;
}
else if (inputType == FormatType.Xml)
{
XmlFormatReader reader = XmlFormatReader.CreateInstance(input);
reader.RootElementName = options.XmlReaderRootElementName;
reader.Options = options.XmlReaderOptions;
- codedInput = reader.ReadStartMessage();
+ codedInput = reader;
}
else
throw new NotSupportedException();
@@ -56,6 +56,7 @@ namespace Google.ProtocolBuffers.Serialization
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();
return (TBuilder)builder.WeakMergeFrom(codedInput, options.ExtensionRegistry);
}
@@ -82,13 +83,12 @@ namespace Google.ProtocolBuffers.Serialization
{
writer.Formatted();
}
- writer.StartMessage();
codedOutput = writer;
}
else if (outputType == FormatType.Xml)
{
XmlFormatWriter writer;
- if (options.FormattedOutput)
+ if (!options.FormattedOutput)
{
writer = XmlFormatWriter.CreateInstance(output);
}
@@ -99,16 +99,15 @@ namespace Google.ProtocolBuffers.Serialization
CheckCharacters = false,
NewLineHandling = NewLineHandling.Entitize,
OmitXmlDeclaration = true,
- Encoding = Encoding.UTF8,
+ Encoding = new UTF8Encoding(false),
Indent = true,
- IndentChars = " ",
+ IndentChars = " ",
NewLineChars = Environment.NewLine,
};
writer = XmlFormatWriter.CreateInstance(XmlWriter.Create(output, settings));
}
writer.RootElementName = options.XmlWriterRootElementName;
writer.Options = options.XmlWriterOptions;
- writer.StartMessage();
codedOutput = writer;
}
else
@@ -126,19 +125,17 @@ namespace Google.ProtocolBuffers.Serialization
/// <param name="output">The stream to write the message to</param>
public static void WriteTo(this IMessageLite message, MessageFormatOptions options, string contentType, Stream output)
{
- using (ICodedOutputStream codedOutput = CreateOutputStream(options, contentType, output))
- {
- message.WriteTo(codedOutput);
+ ICodedOutputStream codedOutput = CreateOutputStream(options, contentType, output);
- // 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();
+ // Output the appropriate message preamble
+ codedOutput.WriteMessageStart();
- codedOutput.Flush();
- }
- }
+ // Write the message content to the output
+ message.WriteTo(codedOutput);
+ // Write the closing message fragment
+ codedOutput.WriteMessageEnd();
+ }
enum FormatType { ProtoBuffer, Json, Xml };
diff --git a/src/ProtocolBuffers.Serialization/Http/MessageFormatOptions.cs b/src/ProtocolBuffers.Serialization/Http/MessageFormatOptions.cs
index 5b88ac94..02f2ea46 100644
--- a/src/ProtocolBuffers.Serialization/Http/MessageFormatOptions.cs
+++ b/src/ProtocolBuffers.Serialization/Http/MessageFormatOptions.cs
@@ -1,6 +1,6 @@
using System;
-namespace Google.ProtocolBuffers.Serialization
+namespace Google.ProtocolBuffers.Serialization.Http
{
/// <summary>
/// Defines control information for the various formatting used with HTTP services
diff --git a/src/ProtocolBuffers.Serialization/Http/ServiceExtensions.cs b/src/ProtocolBuffers.Serialization/Http/ServiceExtensions.cs
index 3ca9964a..022cfb6a 100644
--- a/src/ProtocolBuffers.Serialization/Http/ServiceExtensions.cs
+++ b/src/ProtocolBuffers.Serialization/Http/ServiceExtensions.cs
@@ -3,7 +3,7 @@ using System.Text;
using Google.ProtocolBuffers;
using System.IO;
-namespace Google.ProtocolBuffers.Serialization
+namespace Google.ProtocolBuffers.Serialization.Http
{
/// <summary>
/// Extensions for the IRpcServerStub
@@ -25,6 +25,7 @@ namespace Google.ProtocolBuffers.Serialization
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);
response.WriteTo(options, responseType, output);
}
diff --git a/src/ProtocolBuffers.Serialization/JsonFormatReader.cs b/src/ProtocolBuffers.Serialization/JsonFormatReader.cs
index 40336787..240ce625 100644
--- a/src/ProtocolBuffers.Serialization/JsonFormatReader.cs
+++ b/src/ProtocolBuffers.Serialization/JsonFormatReader.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
@@ -103,19 +103,18 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary>
/// Reads the root-message preamble specific to this formatter
/// </summary>
- public override AbstractReader ReadStartMessage()
+ public override void ReadMessageStart()
{
_input.Consume('{');
_stopChar.Push('}');
_state = ReaderState.BeginObject;
- return this;
}
/// <summary>
/// Reads the root-message close specific to this formatter
/// </summary>
- public override void ReadEndMessage()
+ public override void ReadMessageEnd()
{
_input.Consume((char)_stopChar.Pop());
_state = ReaderState.EndValue;
@@ -126,9 +125,9 @@ namespace Google.ProtocolBuffers.Serialization
/// </summary>
public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
{
- AbstractReader rdr = ReadStartMessage();
- builder.WeakMergeFrom(rdr, registry);
- rdr.ReadEndMessage();
+ ReadMessageStart();
+ builder.WeakMergeFrom(this, registry);
+ ReadMessageEnd();
return builder;
}
diff --git a/src/ProtocolBuffers.Serialization/JsonFormatWriter.cs b/src/ProtocolBuffers.Serialization/JsonFormatWriter.cs
index 5f396ae5..f387f39d 100644
--- a/src/ProtocolBuffers.Serialization/JsonFormatWriter.cs
+++ b/src/ProtocolBuffers.Serialization/JsonFormatWriter.cs
@@ -1,4 +1,4 @@
-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
@@ -244,9 +244,10 @@ namespace Google.ProtocolBuffers.Serialization
/// </summary>
protected override void Dispose(bool disposing)
{
- if (disposing && _counter.Count == 1)
+ if (disposing)
{
- EndMessage();
+ while(_counter.Count > 1)
+ WriteMessageEnd();
}
base.Dispose(disposing);
@@ -458,17 +459,17 @@ namespace Google.ProtocolBuffers.Serialization
/// </summary>
public override void WriteMessage(IMessageLite message)
{
- StartMessage();
+ WriteMessageStart();
message.WriteTo(this);
- EndMessage();
+ 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 EndMessage().
+ /// a call to WriteMessageEnd().
/// </summary>
- public override void StartMessage()
+ public override void WriteMessageStart()
{
if (_isArray)
{
@@ -479,9 +480,9 @@ namespace Google.ProtocolBuffers.Serialization
}
/// <summary>
- /// Used to complete a root-message previously started with a call to StartMessage()
+ /// Used to complete a root-message previously started with a call to WriteMessageStart()
/// </summary>
- public override void EndMessage()
+ public override void WriteMessageEnd()
{
_counter.RemoveAt(_counter.Count - 1);
WriteLine("}");
diff --git a/src/ProtocolBuffers.Serialization/XmlFormatReader.cs b/src/ProtocolBuffers.Serialization/XmlFormatReader.cs
index 0d3bca67..98b69776 100644
--- a/src/ProtocolBuffers.Serialization/XmlFormatReader.cs
+++ b/src/ProtocolBuffers.Serialization/XmlFormatReader.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
@@ -14,8 +14,23 @@ namespace Google.ProtocolBuffers.Serialization
{
public const string DefaultRootElementName = XmlFormatWriter.DefaultRootElementName;
private readonly XmlReader _input;
+ private readonly Stack<ElementStack> _elements;
private string _rootElementName;
+ private struct ElementStack
+ {
+ public readonly string LocalName;
+ public readonly int Depth;
+ public readonly bool IsEmpty;
+
+ public ElementStack(string localName, int depth, bool isEmpty) : this()
+ {
+ LocalName = localName;
+ IsEmpty = isEmpty;
+ Depth = depth;
+ }
+ }
+
private static XmlReaderSettings DefaultSettings
{
get
@@ -72,21 +87,11 @@ namespace Google.ProtocolBuffers.Serialization
{
_input = input;
_rootElementName = DefaultRootElementName;
+ _elements = new Stack<ElementStack>();
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>
public XmlReaderOptions Options { get; set; }
@@ -113,20 +118,6 @@ namespace Google.ProtocolBuffers.Serialization
}
}
- private XmlFormatReader CloneWith(XmlReader rdr)
- {
- XmlFormatReader copy = new XmlFormatReader(this, rdr);
- return copy;
- }
-
- private void NextElement()
- {
- while (!_input.IsStartElement() && _input.Read())
- {
- continue;
- }
- }
-
private static void Assert(bool cond)
{
if (!cond)
@@ -138,36 +129,49 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary>
/// Reads the root-message preamble specific to this formatter
/// </summary>
- public override AbstractReader ReadStartMessage()
+ public override void ReadMessageStart()
{
- return ReadStartMessage(_rootElementName);
+ ReadMessageStart(_rootElementName);
}
- public AbstractReader ReadStartMessage(string element)
+ /// <summary>
+ /// Reads the root-message preamble specific to this formatter
+ /// </summary>
+ public void ReadMessageStart(string element)
{
- string field;
- Assert(PeekNext(out field) && field == element);
-
- XmlReader child = _input.ReadSubtree();
- while (!child.IsStartElement() && child.Read())
+ while (!_input.IsStartElement() && _input.Read())
{
continue;
}
- child.Read();
- return CloneWith(child);
+ Assert(_input.IsStartElement() && _input.LocalName == element);
+ _elements.Push(new ElementStack(element, _input.Depth, _input.IsEmptyElement));
+ _input.Read();
}
/// <summary>
/// Reads the root-message close specific to this formatter, MUST be called
- /// on the reader obtained from ReadStartMessage(string element).
+ /// on the reader obtained from ReadMessageStart(string element).
/// </summary>
- public override void ReadEndMessage()
+ public override void ReadMessageEnd()
{
- Assert(0 == _input.Depth);
- if(_input.NodeType == XmlNodeType.EndElement)
+ Assert(_elements.Count > 0);
+
+ ElementStack stop = _elements.Peek();
+ while (_input.NodeType != XmlNodeType.EndElement && _input.NodeType != XmlNodeType.Element
+ && _input.Depth > stop.Depth && _input.Read())
{
+ continue;
+ }
+
+ if (!stop.IsEmpty)
+ {
+ Assert(_input.NodeType == XmlNodeType.EndElement
+ && _input.LocalName == stop.LocalName
+ && _input.Depth == stop.Depth);
+
_input.Read();
}
+ _elements.Pop();
}
/// <summary>
@@ -192,9 +196,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;
}
@@ -207,7 +211,21 @@ namespace Google.ProtocolBuffers.Serialization
/// </remarks>
protected override bool PeekNext(out string field)
{
- NextElement();
+ ElementStack stopNode;
+ if (_elements.Count == 0)
+ {
+ stopNode = new ElementStack(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;
@@ -270,20 +288,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;
}
@@ -305,27 +312,16 @@ 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())
- {
- continue;
- }
- child.Read();
-
- foreach (string item in CloneWith(child).NonNestedArrayItems("item"))
+ ReadMessageStart(field);
+ foreach (string item in NonNestedArrayItems("item"))
{
yield return item;
}
- Assert(depth == _input.Depth && _input.NodeType == XmlNodeType.EndElement);
+ ReadMessageEnd();
}
- _input.Read();
- yield break;
}
}
} \ No newline at end of file
diff --git a/src/ProtocolBuffers.Serialization/XmlFormatWriter.cs b/src/ProtocolBuffers.Serialization/XmlFormatWriter.cs
index a9cfcc1e..79f403df 100644
--- a/src/ProtocolBuffers.Serialization/XmlFormatWriter.cs
+++ b/src/ProtocolBuffers.Serialization/XmlFormatWriter.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections;
using System.IO;
using System.Text;
@@ -14,10 +14,12 @@ 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;
private string _rootElementName;
+ private int _messageOpenCount;
private static XmlWriterSettings DefaultSettings(Encoding encoding)
{
@@ -43,7 +45,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,6 +67,7 @@ namespace Google.ProtocolBuffers.Serialization
protected XmlFormatWriter(XmlWriter output)
{
_output = output;
+ _messageOpenCount = 0;
_rootElementName = DefaultRootElementName;
}
@@ -75,8 +78,8 @@ namespace Google.ProtocolBuffers.Serialization
{
if (disposing)
{
- if (_output.WriteState != WriteState.Closed && _output.WriteState != WriteState.Start)
- _output.WriteEndDocument();
+ while(_messageOpenCount > 0)
+ WriteMessageEnd();
_output.Close();
}
@@ -128,9 +131,9 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary>
/// 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 EndMessage().
+ /// complete the message with a call to WriteMessageEnd().
/// </summary>
- public override void StartMessage()
+ public override void WriteMessageStart()
{
StartMessage(_rootElementName);
}
@@ -138,7 +141,7 @@ namespace Google.ProtocolBuffers.Serialization
/// <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 EndMessage().
+ /// a call to WriteMessageEnd().
/// </summary>
public void StartMessage(string elementName)
{
@@ -151,15 +154,20 @@ namespace Google.ProtocolBuffers.Serialization
{
_output.WriteStartElement(elementName);
}
+ _messageOpenCount++;
}
/// <summary>
- /// Used to complete a root-message previously started with a call to StartMessage()
+ /// Used to complete a root-message previously started with a call to WriteMessageStart()
/// </summary>
- public override void EndMessage()
+ public override void WriteMessageEnd()
{
+ if (_messageOpenCount <= 0)
+ throw new InvalidOperationException();
+
_output.WriteEndElement();
_output.Flush();
+ _messageOpenCount--;
}
/// <summary>
@@ -177,7 +185,7 @@ namespace Google.ProtocolBuffers.Serialization
{
StartMessage(elementName);
message.WriteTo(this);
- EndMessage();
+ WriteMessageEnd();
}
/// <summary>
diff --git a/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj b/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
index 95ab0b9c..549fe88c 100644
--- a/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
+++ b/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
@@ -106,6 +106,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReflectionTester.cs" />
<Compile Include="ServiceTest.cs" />
+ <Compile Include="TestMimeMessageFormats.cs" />
<Compile Include="TestProtos\UnitTestCSharpOptionsProtoFile.cs" />
<Compile Include="TestProtos\UnitTestCustomOptionsProtoFile.cs" />
<Compile Include="TestProtos\UnitTestEmbedOptimizeForProtoFile.cs" />
diff --git a/src/ProtocolBuffers.Test/TestMimeMessageFormats.cs b/src/ProtocolBuffers.Test/TestMimeMessageFormats.cs
new file mode 100644
index 00000000..6f4b7e0f
--- /dev/null
+++ b/src/ProtocolBuffers.Test/TestMimeMessageFormats.cs
@@ -0,0 +1,225 @@
+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()));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ProtocolBuffers.Test/TestWriterFormatJson.cs b/src/ProtocolBuffers.Test/TestWriterFormatJson.cs
index 341f0b3c..1958df0d 100644
--- a/src/ProtocolBuffers.Test/TestWriterFormatJson.cs
+++ b/src/ProtocolBuffers.Test/TestWriterFormatJson.cs
@@ -45,7 +45,7 @@ namespace Google.ProtocolBuffers
using (TextWriter output = new StringWriter())
using (AbstractWriter writer = JsonFormatWriter.CreateInstance(output))
{
- writer.StartMessage(); //manually begin the message, output is '{'
+ writer.WriteMessageStart(); //manually begin the message, output is '{'
writer.Flush();
Assert.AreEqual("{", output.ToString());
@@ -56,7 +56,7 @@ namespace Google.ProtocolBuffers
writer.Flush();
Assert.AreEqual(@"{""valid"":true", output.ToString());
- writer.EndMessage(); //manually write the end message '}'
+ writer.WriteMessageEnd(); //manually write the end message '}'
Assert.AreEqual(@"{""valid"":true}", output.ToString());
}
}
@@ -65,13 +65,13 @@ namespace Google.ProtocolBuffers
public void Example_ReadJsonUsingICodedInputStream()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
- AbstractReader reader = JsonFormatReader.CreateInstance(@"{""valid"":true}");
+ ICodedInputStream reader = JsonFormatReader.CreateInstance(@"{""valid"":true}");
- AbstractReader stream = reader.ReadStartMessage(); //manually read the begin the message '{'
+ reader.ReadMessageStart(); //manually read the begin the message '{'
- builder.MergeFrom(stream); //write the message normally
+ builder.MergeFrom(reader); //write the message normally
- stream.ReadEndMessage(); //manually read the end message '}'
+ reader.ReadMessageEnd(); //manually read the end message '}'
}
protected string Content;
@@ -401,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()
{
diff --git a/src/ProtocolBuffers.Test/TestWriterFormatXml.cs b/src/ProtocolBuffers.Test/TestWriterFormatXml.cs
index d56cc525..f401481e 100644
--- a/src/ProtocolBuffers.Test/TestWriterFormatXml.cs
+++ b/src/ProtocolBuffers.Test/TestWriterFormatXml.cs
@@ -48,12 +48,12 @@ namespace Google.ProtocolBuffers
using (TextWriter output = new StringWriter())
using (AbstractWriter writer = XmlFormatWriter.CreateInstance(output))
{
- writer.StartMessage(); //manually begin the message, output is '{'
+ writer.WriteMessageStart(); //manually begin the message, output is '{'
ICodedOutputStream stream = writer;
- message.WriteTo(stream); //write the message normally
+ message.WriteTo(stream); //write the message normally
- writer.EndMessage(); //manually write the end message '}'
+ writer.WriteMessageEnd(); //manually write the end message '}'
Assert.AreEqual(@"<root><valid>true</valid></root>", output.ToString());
}
}
@@ -62,13 +62,13 @@ namespace Google.ProtocolBuffers
public void Example_ReadXmlUsingICodedInputStream()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
- AbstractReader reader = XmlFormatReader.CreateInstance(@"<root><valid>true</valid></root>");
+ ICodedInputStream reader = XmlFormatReader.CreateInstance(@"<root><valid>true</valid></root>");
- AbstractReader stream = reader.ReadStartMessage(); //manually read the begin the message '{'
+ reader.ReadMessageStart(); //manually read the begin the message '{'
- builder.MergeFrom(stream); //write the message normally
+ builder.MergeFrom(reader); //read the message normally
- stream.ReadEndMessage(); //manually read the end message '}'
+ reader.ReadMessageEnd(); //manually read the end message '}'
}
[Test]
@@ -387,13 +387,54 @@ namespace Google.ProtocolBuffers
public void TestXmlReadEmptyRoot()
{
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder();
- AbstractReader reader = XmlFormatReader.CreateInstance(@"<root/>");
+ ICodedInputStream reader = XmlFormatReader.CreateInstance(@"<root/>");
- AbstractReader stream = reader.ReadStartMessage(); //manually read the begin the message '{'
+ reader.ReadMessageStart(); //manually read the begin the message '{'
- builder.MergeFrom(stream); //write the message normally
+ builder.MergeFrom(reader); //write the message normally
- stream.ReadEndMessage(); //manually read the end message '}'
+ 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))]
diff --git a/src/ProtocolBuffers/CodedInputStream.cs b/src/ProtocolBuffers/CodedInputStream.cs
index 509b4164..35ebba92 100644
--- a/src/ProtocolBuffers/CodedInputStream.cs
+++ b/src/ProtocolBuffers/CodedInputStream.cs
@@ -144,6 +144,9 @@ namespace Google.ProtocolBuffers
#endregion
+ void ICodedInputStream.ReadMessageStart() { }
+ void ICodedInputStream.ReadMessageEnd() { }
+
#region Validation
/// <summary>
diff --git a/src/ProtocolBuffers/CodedOutputStream.cs b/src/ProtocolBuffers/CodedOutputStream.cs
index bb133a77..560719af 100644
--- a/src/ProtocolBuffers/CodedOutputStream.cs
+++ b/src/ProtocolBuffers/CodedOutputStream.cs
@@ -136,6 +136,9 @@ namespace Google.ProtocolBuffers
}
}
+ void ICodedOutputStream.WriteMessageStart() { }
+ void ICodedOutputStream.WriteMessageEnd() { Flush(); }
+
#region Writing of unknown fields
[Obsolete]
diff --git a/src/ProtocolBuffers/ICodedInputStream.cs b/src/ProtocolBuffers/ICodedInputStream.cs
index 1d9d26e3..a3c0f30b 100644
--- a/src/ProtocolBuffers/ICodedInputStream.cs
+++ b/src/ProtocolBuffers/ICodedInputStream.cs
@@ -46,6 +46,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.
/// </summary>
diff --git a/src/ProtocolBuffers/ICodedOutputStream.cs b/src/ProtocolBuffers/ICodedOutputStream.cs
index 2c324792..a686fed1 100644
--- a/src/ProtocolBuffers/ICodedOutputStream.cs
+++ b/src/ProtocolBuffers/ICodedOutputStream.cs
@@ -52,6 +52,24 @@ namespace Google.ProtocolBuffers
public interface ICodedOutputStream : IDisposable
{
/// <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>
void Flush();