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 ...@@ -17,12 +17,6 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary> Constructs a new reader </summary> /// <summary> Constructs a new reader </summary>
protected AbstractReader() { MaxDepth = DefaultMaxDepth; } 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> /// <summary> Gets or sets the maximum recursion depth allowed </summary>
public int MaxDepth { get; set; } public int MaxDepth { get; set; }
...@@ -112,6 +106,16 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -112,6 +106,16 @@ namespace Google.ProtocolBuffers.Serialization
/// Merges the input stream into the provided IBuilderLite /// Merges the input stream into the provided IBuilderLite
/// </summary> /// </summary>
protected abstract bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry); 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> /// <summary>
/// Merges the input stream into the provided IBuilderLite /// Merges the input stream into the provided IBuilderLite
......
...@@ -11,10 +11,6 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -11,10 +11,6 @@ namespace Google.ProtocolBuffers.Serialization
{ {
/// <summary> Constructs a new reader </summary> /// <summary> Constructs a new reader </summary>
protected AbstractTextReader() { } protected AbstractTextReader() { }
/// <summary> Constructs a new child reader </summary>
protected AbstractTextReader(AbstractTextReader copyFrom)
: base(copyFrom)
{ }
/// <summary> /// <summary>
/// Reads a typed field as a string /// Reads a typed field as a string
......
...@@ -12,36 +12,39 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -12,36 +12,39 @@ namespace Google.ProtocolBuffers.Serialization
/// <summary> /// <summary>
/// Provides a base class for writers that performs some basic type dispatching /// Provides a base class for writers that performs some basic type dispatching
/// </summary> /// </summary>
public abstract class AbstractWriter : ICodedOutputStream, IDisposable public abstract class AbstractWriter : ICodedOutputStream
{ {
/// <summary> /// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// Completes any pending write operations
/// </summary> /// </summary>
public void Dispose() public virtual void Flush()
{ {
GC.SuppressFinalize(this);
Flush();
Dispose(true);
} }
/// <summary> /// <summary>
/// Completes any pending write operations /// Writes the message to the the formatted stream.
/// </summary> /// </summary>
public virtual void Flush() public abstract void WriteMessage(IMessageLite message);
{
}
/// <summary> /// <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> /// </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> /// <summary>
/// Writes the message to the the formatted stream. /// Used to complete a root-message previously started with a call to WriteMessageStart()
/// </summary> /// </summary>
public abstract void WriteMessage(IMessageLite message); public abstract void WriteMessageEnd();
/// <summary> /// <summary>
/// Writes a Boolean value /// Writes a Boolean value
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using Google.ProtocolBuffers.Descriptors; using Google.ProtocolBuffers.Descriptors;
...@@ -22,6 +22,18 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -22,6 +22,18 @@ namespace Google.ProtocolBuffers.Serialization
_ready = _input.MoveNext(); _ready = _input.MoveNext();
} }
/// <summary>
/// No-op
/// </summary>
public override void ReadMessageStart()
{ }
/// <summary>
/// No-op
/// </summary>
public override void ReadMessageEnd()
{ }
/// <summary> /// <summary>
/// Merges the contents of stream into the provided message builder /// Merges the contents of stream into the provided message builder
/// </summary> /// </summary>
......
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Google.ProtocolBuffers.Descriptors; using Google.ProtocolBuffers.Descriptors;
...@@ -53,6 +53,19 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -53,6 +53,19 @@ namespace Google.ProtocolBuffers.Serialization
message.WriteTo(this); message.WriteTo(this);
} }
/// <summary>
/// No-op
/// </summary>
public override void WriteMessageStart()
{ }
/// <summary>
/// No-op
/// </summary>
public override void WriteMessageEnd()
{ }
/// <summary> /// <summary>
/// Writes a Boolean value /// Writes a Boolean value
/// </summary> /// </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.Collections.Generic;
using System.IO; using System.IO;
using System.Xml; using System.Xml;
...@@ -11,6 +11,7 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -11,6 +11,7 @@ namespace Google.ProtocolBuffers.Serialization
public class JsonFormatReader : AbstractTextReader public class JsonFormatReader : AbstractTextReader
{ {
private readonly JsonCursor _input; private readonly JsonCursor _input;
// The expected token that ends the current item, either ']' or '}'
private readonly Stack<int> _stopChar; private readonly Stack<int> _stopChar;
private enum ReaderState private enum ReaderState
...@@ -101,17 +102,33 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -101,17 +102,33 @@ namespace Google.ProtocolBuffers.Serialization
} }
/// <summary> /// <summary>
/// Merges the contents of stream into the provided message builder /// Reads the root-message preamble specific to this formatter
/// </summary> /// </summary>
public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry) public override void ReadMessageStart()
{ {
_input.Consume('{'); _input.Consume('{');
_stopChar.Push('}'); _stopChar.Push('}');
_state = ReaderState.BeginObject; _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; _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; return builder;
} }
......
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
...@@ -101,7 +101,7 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -101,7 +101,7 @@ namespace Google.ProtocolBuffers.Serialization
private class JsonStreamWriter : JsonFormatWriter private class JsonStreamWriter : JsonFormatWriter
{ {
#if SILVERLIGHT2 || COMPACT_FRAMEWORK_35 #if SILVERLIGHT2 || COMPACT_FRAMEWORK_35
static readonly Encoding Encoding = Encoding.UTF8; static readonly Encoding Encoding = new UTF8Encoding(false);
#else #else
private static readonly Encoding Encoding = Encoding.ASCII; private static readonly Encoding Encoding = Encoding.ASCII;
#endif #endif
...@@ -168,7 +168,9 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -168,7 +168,9 @@ namespace Google.ProtocolBuffers.Serialization
#endregion #endregion
//Tracks the writer depth and the array element count at that depth.
private readonly List<int> _counter; 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; private bool _isArray;
/// <summary> /// <summary>
...@@ -243,7 +245,7 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -243,7 +245,7 @@ namespace Google.ProtocolBuffers.Serialization
{ {
if (_counter.Count == 0) 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; int index = _counter.Count - 1;
...@@ -444,6 +446,18 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -444,6 +446,18 @@ namespace Google.ProtocolBuffers.Serialization
/// Writes the message to the the formatted stream. /// Writes the message to the the formatted stream.
/// </summary> /// </summary>
public override void WriteMessage(IMessageLite message) 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) if (_isArray)
{ {
...@@ -451,7 +465,13 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -451,7 +465,13 @@ namespace Google.ProtocolBuffers.Serialization
} }
WriteToOutput("{"); WriteToOutput("{");
_counter.Add(0); _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); _counter.RemoveAt(_counter.Count - 1);
WriteLine("}"); WriteLine("}");
Flush(); Flush();
......
...@@ -98,6 +98,10 @@ ...@@ -98,6 +98,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Extensions.cs" /> <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="Properties\AssemblyInfo.cs" />
<Compile Include="AbstractReader.cs" /> <Compile Include="AbstractReader.cs" />
<Compile Include="AbstractTextReader.cs" /> <Compile Include="AbstractTextReader.cs" />
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Xml; using System.Xml;
using System.Diagnostics;
namespace Google.ProtocolBuffers.Serialization namespace Google.ProtocolBuffers.Serialization
{ {
...@@ -14,8 +15,25 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -14,8 +15,25 @@ namespace Google.ProtocolBuffers.Serialization
{ {
public const string DefaultRootElementName = XmlFormatWriter.DefaultRootElementName; public const string DefaultRootElementName = XmlFormatWriter.DefaultRootElementName;
private readonly XmlReader _input; 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 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 private static XmlReaderSettings DefaultSettings
{ {
get get
...@@ -72,20 +90,10 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -72,20 +90,10 @@ namespace Google.ProtocolBuffers.Serialization
{ {
_input = input; _input = input;
_rootElementName = DefaultRootElementName; _rootElementName = DefaultRootElementName;
_elements = new Stack<ElementStackEntry>();
Options = XmlReaderOptions.None; 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> /// <summary>
/// Gets or sets the options to use when reading the xml /// Gets or sets the options to use when reading the xml
/// </summary> /// </summary>
...@@ -113,26 +121,61 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -113,26 +121,61 @@ namespace Google.ProtocolBuffers.Serialization
} }
} }
private XmlFormatReader CloneWith(XmlReader rdr) [DebuggerNonUserCode]
private static void Assert(bool cond)
{ {
XmlFormatReader copy = new XmlFormatReader(this, rdr); if (!cond)
return copy; {
throw new FormatException();
}
} }
private void NextElement() /// <summary>
/// Reads the root-message preamble specific to this formatter
/// </summary>
public override void ReadMessageStart()
{
ReadMessageStart(_rootElementName);
}
/// <summary>
/// Reads the root-message preamble specific to this formatter
/// </summary>
public void ReadMessageStart(string element)
{ {
while (!_input.IsStartElement() && _input.Read()) while (!_input.IsStartElement() && _input.Read())
{ {
continue; 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> /// <summary>
...@@ -157,9 +200,9 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -157,9 +200,9 @@ namespace Google.ProtocolBuffers.Serialization
public TBuilder Merge<TBuilder>(string element, TBuilder builder, ExtensionRegistry registry) public TBuilder Merge<TBuilder>(string element, TBuilder builder, ExtensionRegistry registry)
where TBuilder : IBuilderLite where TBuilder : IBuilderLite
{ {
string field; ReadMessageStart(element);
Assert(PeekNext(out field) && field == element); builder.WeakMergeFrom(this, registry);
ReadMessage(builder, registry); ReadMessageEnd();
return builder; return builder;
} }
...@@ -172,7 +215,21 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -172,7 +215,21 @@ namespace Google.ProtocolBuffers.Serialization
/// </remarks> /// </remarks>
protected override bool PeekNext(out string field) 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()) if (_input.IsStartElement())
{ {
field = _input.LocalName; field = _input.LocalName;
...@@ -235,20 +292,9 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -235,20 +292,9 @@ namespace Google.ProtocolBuffers.Serialization
protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry) protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
{ {
Assert(_input.IsStartElement()); Assert(_input.IsStartElement());
ReadMessageStart(_input.LocalName);
if (!_input.IsEmptyElement) builder.WeakMergeFrom(this, registry);
{ ReadMessageEnd();
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();
return true; return true;
} }
...@@ -270,27 +316,20 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -270,27 +316,20 @@ namespace Google.ProtocolBuffers.Serialization
{ {
yield return item; yield return item;
} }
yield break;
} }
if (!_input.IsEmptyElement) else
{ {
int depth = _input.Depth; string found;
XmlReader child = _input.ReadSubtree(); ReadMessageStart(field);
if (PeekNext(out found) && found == "item")
while (!child.IsStartElement() && child.Read())
{ {
continue; foreach (string item in NonNestedArrayItems("item"))
} {
child.Read(); yield return item;
}
foreach (string item in CloneWith(child).NonNestedArrayItems("item"))
{
yield return item;
} }
Assert(depth == _input.Depth && _input.NodeType == XmlNodeType.EndElement); ReadMessageEnd();
} }
_input.Read();
yield break;
} }
} }
} }
\ No newline at end of file
using System; using System;
using System.Collections; using System.Collections;
using System.IO; using System.IO;
using System.Text; using System.Text;
...@@ -14,10 +14,14 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -14,10 +14,14 @@ namespace Google.ProtocolBuffers.Serialization
/// </summary> /// </summary>
public class XmlFormatWriter : AbstractTextWriter public class XmlFormatWriter : AbstractTextWriter
{ {
private static readonly Encoding DefaultEncoding = new UTF8Encoding(false);
public const string DefaultRootElementName = "root"; public const string DefaultRootElementName = "root";
private const int NestedArrayFlag = 0x0001;
private readonly XmlWriter _output; private readonly XmlWriter _output;
// The default element name used for WriteMessageStart
private string _rootElementName; private string _rootElementName;
// Used to assert matching WriteMessageStart/WriteMessageEnd calls
private int _messageOpenCount;
private static XmlWriterSettings DefaultSettings(Encoding encoding) private static XmlWriterSettings DefaultSettings(Encoding encoding)
{ {
...@@ -43,7 +47,7 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -43,7 +47,7 @@ namespace Google.ProtocolBuffers.Serialization
/// </summary> /// </summary>
public static XmlFormatWriter CreateInstance(Stream output) public static XmlFormatWriter CreateInstance(Stream output)
{ {
return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(Encoding.UTF8))); return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(DefaultEncoding)));
} }
/// <summary> /// <summary>
...@@ -65,20 +69,10 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -65,20 +69,10 @@ namespace Google.ProtocolBuffers.Serialization
protected XmlFormatWriter(XmlWriter output) protected XmlFormatWriter(XmlWriter output)
{ {
_output = output; _output = output;
_messageOpenCount = 0;
_rootElementName = DefaultRootElementName; _rootElementName = DefaultRootElementName;
} }
/// <summary>
/// Closes the underlying XmlTextWriter
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
_output.Close();
}
}
/// <summary> /// <summary>
/// Gets or sets the default element name to use when using the Merge&lt;TBuilder>() /// Gets or sets the default element name to use when using the Merge&lt;TBuilder>()
/// </summary> /// </summary>
...@@ -112,17 +106,30 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -112,17 +106,30 @@ namespace Google.ProtocolBuffers.Serialization
} }
/// <summary> /// <summary>
/// Writes a message as an element using the name defined in <see cref="RootElementName"/> /// Completes any pending write operations
/// </summary> /// </summary>
public override void WriteMessage(IMessageLite message) public override void Flush()
{ {
WriteMessage(_rootElementName, message); _output.Flush();
base.Flush();
} }
/// <summary> /// <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> /// </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)) if (TestOption(XmlWriterOptions.OutputJsonTypes))
{ {
...@@ -133,10 +140,40 @@ namespace Google.ProtocolBuffers.Serialization ...@@ -133,10 +140,40 @@ namespace Google.ProtocolBuffers.Serialization
{ {
_output.WriteStartElement(elementName); _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.WriteEndElement();
_output.Flush(); _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> /// <summary>
......
...@@ -7,6 +7,24 @@ namespace Google.ProtocolBuffers.Compatibility ...@@ -7,6 +7,24 @@ namespace Google.ProtocolBuffers.Compatibility
{ {
[TestFixture] [TestFixture]
public class JsonCompatibilityTests : CompatibilityTests 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) protected override object SerializeMessage<TMessage, TBuilder>(TMessage message)
{ {
......
using System.IO; using System.IO;
using System.Xml;
using Google.ProtocolBuffers.Serialization; using Google.ProtocolBuffers.Serialization;
using Google.ProtocolBuffers.TestProtos; using Google.ProtocolBuffers.TestProtos;
using NUnit.Framework; using NUnit.Framework;
...@@ -22,4 +23,24 @@ namespace Google.ProtocolBuffers.Compatibility ...@@ -22,4 +23,24 @@ namespace Google.ProtocolBuffers.Compatibility
return reader.Merge("root", builder, registry); 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; ...@@ -12,7 +12,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ProtocolBuffers.Test")] [assembly: AssemblyProduct("ProtocolBuffers.Test")]
[assembly: AssemblyCopyright("Copyright © 2008")] [assembly: AssemblyCopyright("Copyright 2008")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
......
...@@ -88,6 +88,8 @@ ...@@ -88,6 +88,8 @@
</Compile> </Compile>
<Compile Include="Compatibility\TextCompatibilityTests.cs" /> <Compile Include="Compatibility\TextCompatibilityTests.cs" />
<Compile Include="Compatibility\XmlCompatibilityTests.cs" /> <Compile Include="Compatibility\XmlCompatibilityTests.cs" />
<Compile Include="TestRpcForMimeTypes.cs" />
<Compile Include="TestReaderForUrlEncoded.cs" />
<Compile Include="CSharpOptionsTest.cs" /> <Compile Include="CSharpOptionsTest.cs" />
<Compile Include="DeprecatedMemberTest.cs" /> <Compile Include="DeprecatedMemberTest.cs" />
<Compile Include="DescriptorsTest.cs" /> <Compile Include="DescriptorsTest.cs" />
...@@ -107,6 +109,7 @@ ...@@ -107,6 +109,7 @@
<Compile Include="SerializableTest.cs" /> <Compile Include="SerializableTest.cs" />
<Compile Include="ServiceTest.cs" /> <Compile Include="ServiceTest.cs" />
<Compile Include="TestCornerCases.cs" /> <Compile Include="TestCornerCases.cs" />
<Compile Include="TestMimeMessageFormats.cs" />
<Compile Include="TestProtos\UnitTestCSharpOptionsProtoFile.cs" /> <Compile Include="TestProtos\UnitTestCSharpOptionsProtoFile.cs" />
<Compile Include="TestProtos\UnitTestCustomOptionsProtoFile.cs" /> <Compile Include="TestProtos\UnitTestCustomOptionsProtoFile.cs" />
<Compile Include="TestProtos\UnitTestEmbedOptimizeForProtoFile.cs" /> <Compile Include="TestProtos\UnitTestEmbedOptimizeForProtoFile.cs" />
......
This diff is collapsed.
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);
}
}
}
This diff is collapsed.
...@@ -10,6 +10,70 @@ namespace Google.ProtocolBuffers ...@@ -10,6 +10,70 @@ namespace Google.ProtocolBuffers
[TestFixture] [TestFixture]
public class TestWriterFormatJson 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; protected string Content;
[System.Diagnostics.DebuggerNonUserCode] [System.Diagnostics.DebuggerNonUserCode]
protected void FormatterAssert<TMessage>(TMessage message, params string[] expecting) where TMessage : IMessageLite protected void FormatterAssert<TMessage>(TMessage message, params string[] expecting) where TMessage : IMessageLite
...@@ -337,6 +401,28 @@ namespace Google.ProtocolBuffers ...@@ -337,6 +401,28 @@ namespace Google.ProtocolBuffers
Assert.AreEqual(3, ordinal); Assert.AreEqual(3, ordinal);
Assert.AreEqual(3, builder.TextlinesCount); 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))] [Test,ExpectedException(typeof(RecursionLimitExceededException))]
public void TestRecursiveLimit() public void TestRecursiveLimit()
{ {
......
...@@ -12,6 +12,65 @@ namespace Google.ProtocolBuffers ...@@ -12,6 +12,65 @@ namespace Google.ProtocolBuffers
[TestFixture] [TestFixture]
public class TestWriterFormatXml 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] [Test]
public void TestToXmlParseFromXml() public void TestToXmlParseFromXml()
{ {
...@@ -135,7 +194,9 @@ namespace Google.ProtocolBuffers ...@@ -135,7 +194,9 @@ namespace Google.ProtocolBuffers
.Build(); .Build();
StringWriter sw = new StringWriter(); 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(); string xml = sw.ToString();
...@@ -162,7 +223,9 @@ namespace Google.ProtocolBuffers ...@@ -162,7 +223,9 @@ namespace Google.ProtocolBuffers
.Build(); .Build();
StringWriter sw = new StringWriter(); 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) .SetOptions(XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputEnumValues)
.WriteMessage("root", message); .WriteMessage("root", message);
...@@ -324,6 +387,60 @@ namespace Google.ProtocolBuffers ...@@ -324,6 +387,60 @@ namespace Google.ProtocolBuffers
TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder(), registry).Build(); TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder(), registry).Build();
Assert.AreEqual(message, copy); 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))] [Test, ExpectedException(typeof(RecursionLimitExceededException))]
public void TestRecursiveLimit() public void TestRecursiveLimit()
{ {
......
...@@ -144,6 +144,9 @@ namespace Google.ProtocolBuffers ...@@ -144,6 +144,9 @@ namespace Google.ProtocolBuffers
#endregion #endregion
void ICodedInputStream.ReadMessageStart() { }
void ICodedInputStream.ReadMessageEnd() { }
#region Validation #region Validation
/// <summary> /// <summary>
......
...@@ -125,6 +125,9 @@ namespace Google.ProtocolBuffers ...@@ -125,6 +125,9 @@ namespace Google.ProtocolBuffers
} }
#endregion #endregion
void ICodedOutputStream.WriteMessageStart() { }
void ICodedOutputStream.WriteMessageEnd() { Flush(); }
#region Writing of unknown fields #region Writing of unknown fields
......
...@@ -45,6 +45,24 @@ namespace Google.ProtocolBuffers ...@@ -45,6 +45,24 @@ namespace Google.ProtocolBuffers
{ {
public interface ICodedInputStream 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> /// <summary>
/// Attempt to read a field tag, returning false if we have reached the end /// Attempt to read a field tag, returning false if we have reached the end
/// of the input data. /// of the input data.
......
...@@ -51,6 +51,24 @@ namespace Google.ProtocolBuffers ...@@ -51,6 +51,24 @@ namespace Google.ProtocolBuffers
/// </summary> /// </summary>
public interface ICodedOutputStream 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> /// <summary>
/// Indicates that all temporary buffers be written to the final output. /// Indicates that all temporary buffers be written to the final output.
/// </summary> /// </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