aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorcsharptest <roger@csharptest.net>2011-06-10 14:41:47 -0500
committerrogerk <devnull@localhost>2011-06-10 14:41:47 -0500
commit2b86884659413efb1cbbcf7ebe22ef46a565b13d (patch)
treee35e2776944153fbcad1a146b4b37ae9ed691a41 /src
parentb00ea13eb1b8055a0cde89d01bbde6d95e5cd1a5 (diff)
downloadprotobuf-2b86884659413efb1cbbcf7ebe22ef46a565b13d.tar.gz
protobuf-2b86884659413efb1cbbcf7ebe22ef46a565b13d.tar.bz2
protobuf-2b86884659413efb1cbbcf7ebe22ef46a565b13d.zip
Added the XmlFormatWriter/Reader
Diffstat (limited to 'src')
-rw-r--r--src/ProtocolBuffers.Test/CompatTests/BinaryCompatibilityTests.cs2
-rw-r--r--src/ProtocolBuffers.Test/CompatTests/CompatibilityTests.cs1
-rw-r--r--src/ProtocolBuffers.Test/CompatTests/TextCompatibilityTests.cs2
-rw-r--r--src/ProtocolBuffers.Test/CompatTests/XmlCompatibilityTests.cs24
-rw-r--r--src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj2
-rw-r--r--src/ProtocolBuffers.Test/TestWriterFormatXml.cs308
-rw-r--r--src/ProtocolBuffers/CodedOutputStream.cs38
-rw-r--r--src/ProtocolBuffers/ICodedOutputStream.cs6
-rw-r--r--src/ProtocolBuffers/ProtocolBuffers.csproj8
-rw-r--r--src/ProtocolBuffers/Serialization/AbstractReader.cs508
-rw-r--r--src/ProtocolBuffers/Serialization/AbstractTextReader.cs171
-rw-r--r--src/ProtocolBuffers/Serialization/AbstractTextWriter.cs73
-rw-r--r--src/ProtocolBuffers/Serialization/AbstractWriter.cs345
-rw-r--r--src/ProtocolBuffers/Serialization/XmlFormatReader.cs222
-rw-r--r--src/ProtocolBuffers/Serialization/XmlFormatWriter.cs166
-rw-r--r--src/ProtocolBuffers/Serialization/XmlReaderOptions.cs16
-rw-r--r--src/ProtocolBuffers/Serialization/XmlWriterOptions.cs21
-rw-r--r--src/ProtocolBuffers/UnknownField.cs10
-rw-r--r--src/ProtocolBuffers/UnknownFieldSet.cs6
19 files changed, 1910 insertions, 19 deletions
diff --git a/src/ProtocolBuffers.Test/CompatTests/BinaryCompatibilityTests.cs b/src/ProtocolBuffers.Test/CompatTests/BinaryCompatibilityTests.cs
index fe8a1e37..c5b78bc9 100644
--- a/src/ProtocolBuffers.Test/CompatTests/BinaryCompatibilityTests.cs
+++ b/src/ProtocolBuffers.Test/CompatTests/BinaryCompatibilityTests.cs
@@ -6,8 +6,6 @@ namespace Google.ProtocolBuffers.CompatTests
[TestFixture]
public class BinaryCompatibilityTests : CompatibilityTests
{
- protected override string TestName { get { return "binary"; } }
-
protected override object SerializeMessage<TMessage, TBuilder>(TMessage message)
{
byte[] bresult = message.ToByteArray();
diff --git a/src/ProtocolBuffers.Test/CompatTests/CompatibilityTests.cs b/src/ProtocolBuffers.Test/CompatTests/CompatibilityTests.cs
index 5ce069be..49c56290 100644
--- a/src/ProtocolBuffers.Test/CompatTests/CompatibilityTests.cs
+++ b/src/ProtocolBuffers.Test/CompatTests/CompatibilityTests.cs
@@ -6,7 +6,6 @@ namespace Google.ProtocolBuffers.CompatTests
{
public abstract class CompatibilityTests
{
- protected abstract string TestName { get; }
protected abstract object SerializeMessage<TMessage, TBuilder>(TMessage message)
where TMessage : IMessageLite<TMessage, TBuilder>
where TBuilder : IBuilderLite<TMessage, TBuilder>;
diff --git a/src/ProtocolBuffers.Test/CompatTests/TextCompatibilityTests.cs b/src/ProtocolBuffers.Test/CompatTests/TextCompatibilityTests.cs
index 83f19ae3..0b575358 100644
--- a/src/ProtocolBuffers.Test/CompatTests/TextCompatibilityTests.cs
+++ b/src/ProtocolBuffers.Test/CompatTests/TextCompatibilityTests.cs
@@ -7,8 +7,6 @@ namespace Google.ProtocolBuffers.CompatTests
[TestFixture]
public class TextCompatibilityTests : CompatibilityTests
{
- protected override string TestName { get { return "text"; } }
-
protected override object SerializeMessage<TMessage, TBuilder>(TMessage message)
{
StringWriter text = new StringWriter();
diff --git a/src/ProtocolBuffers.Test/CompatTests/XmlCompatibilityTests.cs b/src/ProtocolBuffers.Test/CompatTests/XmlCompatibilityTests.cs
new file mode 100644
index 00000000..e204200d
--- /dev/null
+++ b/src/ProtocolBuffers.Test/CompatTests/XmlCompatibilityTests.cs
@@ -0,0 +1,24 @@
+using System.IO;
+using Google.ProtocolBuffers.Serialization;
+using NUnit.Framework;
+
+namespace Google.ProtocolBuffers.CompatTests
+{
+ [TestFixture]
+ public class XmlCompatibilityTests : CompatibilityTests
+ {
+ protected override object SerializeMessage<TMessage, TBuilder>(TMessage message)
+ {
+ StringWriter text = new StringWriter();
+ XmlFormatWriter writer = new XmlFormatWriter(text);
+ writer.WriteMessage("root", message);
+ return text.ToString();
+ }
+
+ protected override TBuilder DeerializeMessage<TMessage, TBuilder>(object message, TBuilder builder, ExtensionRegistry registry)
+ {
+ XmlFormatReader reader = new XmlFormatReader((string)message);
+ return reader.Merge("root", builder, registry);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj b/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
index 06dda010..39b4026c 100644
--- a/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
+++ b/src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj
@@ -83,6 +83,7 @@
<DependentUpon>TestResources.resx</DependentUpon>
</Compile>
<Compile Include="CompatTests\TextCompatibilityTests.cs" />
+ <Compile Include="CompatTests\XmlCompatibilityTests.cs" />
<Compile Include="CSharpOptionsTest.cs" />
<Compile Include="DescriptorsTest.cs" />
<Compile Include="Descriptors\MessageDescriptorTest.cs" />
@@ -114,6 +115,7 @@
<Compile Include="TestProtos\UnitTestXmlSerializerTestProtoFile.cs" />
<Compile Include="TestRpcGenerator.cs" />
<Compile Include="TestUtil.cs" />
+ <Compile Include="TestWriterFormatXml.cs" />
<Compile Include="TextFormatTest.cs" />
<Compile Include="UnknownFieldSetTest.cs" />
<Compile Include="WireFormatTest.cs" />
diff --git a/src/ProtocolBuffers.Test/TestWriterFormatXml.cs b/src/ProtocolBuffers.Test/TestWriterFormatXml.cs
new file mode 100644
index 00000000..a2def5e0
--- /dev/null
+++ b/src/ProtocolBuffers.Test/TestWriterFormatXml.cs
@@ -0,0 +1,308 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Xml;
+using Google.ProtocolBuffers.Serialization;
+using NUnit.Framework;
+using Google.ProtocolBuffers.TestProtos;
+
+namespace Google.ProtocolBuffers
+{
+ [TestFixture]
+ public class TestWriterFormatXml
+ {
+ [Test]
+ public void TestEmptyMessage()
+ {
+ TestXmlChild message = TestXmlChild.CreateBuilder()
+ .Build();
+
+ StringWriter sw = new StringWriter();
+ XmlTextWriter xw = new XmlTextWriter(sw);
+
+ //When we call message.WriteTo, we are responsible for the root element
+ xw.WriteStartElement("root");
+ message.WriteTo(new XmlFormatWriter(xw));
+ xw.WriteEndElement();
+ xw.Flush();
+
+ string xml = sw.ToString();
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlChild copy = rdr.Merge(TestXmlChild.CreateBuilder()).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestRepeatedField()
+ {
+ TestXmlChild message = TestXmlChild.CreateBuilder()
+ .AddOptions(EnumOptions.ONE)
+ .AddOptions(EnumOptions.TWO)
+ .Build();
+
+ //Allow the writer to write the root element
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlChild copy = rdr.Merge(TestXmlChild.CreateBuilder()).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestNestedEmptyMessage()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .SetChild(TestXmlChild.CreateBuilder().Build())
+ .Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder()).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestNestedMessage()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .SetChild(TestXmlChild.CreateBuilder().AddOptions(EnumOptions.TWO).Build())
+ .Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder()).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestBooleanTypes()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .SetValid(true)
+ .Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder()).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestFullMessage()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .SetValid(true)
+ .SetText("text")
+ .AddTextlines("a")
+ .AddTextlines("b")
+ .AddTextlines("c")
+ .SetNumber(0x1010101010)
+ .AddNumbers(1)
+ .AddNumbers(2)
+ .AddNumbers(3)
+ .SetChild(TestXmlChild.CreateBuilder().AddOptions(EnumOptions.ONE).SetBinary(ByteString.CopyFrom(new byte[1])))
+ .AddChildren(TestXmlMessage.Types.Children.CreateBuilder().AddOptions(EnumOptions.TWO).SetBinary(ByteString.CopyFrom(new byte[2])))
+ .AddChildren(TestXmlMessage.Types.Children.CreateBuilder().AddOptions(EnumOptions.THREE).SetBinary(ByteString.CopyFrom(new byte[3])))
+ .Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder()).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestFullMessageWithRichTypes()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .SetValid(true)
+ .SetText("text")
+ .AddTextlines("a")
+ .AddTextlines("b")
+ .AddTextlines("c")
+ .SetNumber(0x1010101010)
+ .AddNumbers(1)
+ .AddNumbers(2)
+ .AddNumbers(3)
+ .SetChild(TestXmlChild.CreateBuilder().AddOptions(EnumOptions.ONE).SetBinary(ByteString.CopyFrom(new byte[1])))
+ .AddChildren(TestXmlMessage.Types.Children.CreateBuilder().AddOptions(EnumOptions.TWO).SetBinary(ByteString.CopyFrom(new byte[2])))
+ .AddChildren(TestXmlMessage.Types.Children.CreateBuilder().AddOptions(EnumOptions.THREE).SetBinary(ByteString.CopyFrom(new byte[3])))
+ .Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw)
+ {
+ Options = XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputEnumValues
+ }.WriteMessage("root", message);
+
+ string xml = sw.ToString();
+
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ rdr.Options = XmlReaderOptions.ReadNestedArrays;
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder()).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestFullMessageWithUnknownFields()
+ {
+ TestXmlMessage origial = TestXmlMessage.CreateBuilder()
+ .SetValid(true)
+ .SetText("text")
+ .AddTextlines("a")
+ .AddTextlines("b")
+ .AddTextlines("c")
+ .SetNumber(0x1010101010)
+ .AddNumbers(1)
+ .AddNumbers(2)
+ .AddNumbers(3)
+ .SetChild(TestXmlChild.CreateBuilder().AddOptions(EnumOptions.ONE).SetBinary(ByteString.CopyFrom(new byte[1])))
+ .AddChildren(TestXmlMessage.Types.Children.CreateBuilder().AddOptions(EnumOptions.TWO).SetBinary(ByteString.CopyFrom(new byte[2])))
+ .AddChildren(TestXmlMessage.Types.Children.CreateBuilder().AddOptions(EnumOptions.THREE).SetBinary(ByteString.CopyFrom(new byte[3])))
+ .Build();
+ TestXmlNoFields message = TestXmlNoFields.CreateBuilder().MergeFrom(origial.ToByteArray()).Build();
+
+ Assert.AreEqual(0, message.AllFields.Count);
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw)
+ {
+ Options = XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputEnumValues
+ }.WriteMessage("root", message);
+
+ string xml = sw.ToString();
+
+ using (XmlReader x = XmlReader.Create(new StringReader(xml)))
+ {
+ x.MoveToContent();
+ Assert.AreEqual(XmlNodeType.Element, x.NodeType);
+ //should always be empty
+ Assert.IsTrue(x.IsEmptyElement ||
+ (x.Read() && x.NodeType == XmlNodeType.EndElement)
+ );
+ }
+
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ rdr.Options = XmlReaderOptions.ReadNestedArrays;
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder()).Build();
+ Assert.AreEqual(TestXmlMessage.DefaultInstance, copy);
+ }
+ [Test]
+ public void TestMessageWithXmlText()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .SetText("<text>").Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder()).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestXmlWithWhitespace()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .SetText(" \t <- leading space and trailing -> \r\n\t").Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder()).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestXmlWithExtensionText()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionText, " extension text value ! ")
+ .Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+
+ ExtensionRegistry registry = ExtensionRegistry.CreateInstance();
+ UnitTestXmlSerializerTestProtoFile.RegisterAllExtensions(registry);
+
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder(), registry).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestXmlWithExtensionMessage()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionMessage,
+ new TestXmlExtension.Builder().SetNumber(42).Build()).Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+
+ ExtensionRegistry registry = ExtensionRegistry.CreateInstance();
+ UnitTestXmlSerializerTestProtoFile.RegisterAllExtensions(registry);
+
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder(), registry).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestXmlWithExtensionArray()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 100)
+ .AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 101)
+ .AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 102)
+ .Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+
+ ExtensionRegistry registry = ExtensionRegistry.CreateInstance();
+ UnitTestXmlSerializerTestProtoFile.RegisterAllExtensions(registry);
+
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder(), registry).Build();
+ Assert.AreEqual(message, copy);
+ }
+ [Test]
+ public void TestXmlWithExtensionEnum()
+ {
+ TestXmlMessage message = TestXmlMessage.CreateBuilder()
+ .SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionEnum, EnumOptions.ONE)
+ .Build();
+
+ StringWriter sw = new StringWriter();
+ new XmlFormatWriter(sw).WriteMessage("root", message);
+
+ string xml = sw.ToString();
+
+ ExtensionRegistry registry = ExtensionRegistry.CreateInstance();
+ UnitTestXmlSerializerTestProtoFile.RegisterAllExtensions(registry);
+
+ XmlFormatReader rdr = new XmlFormatReader(xml);
+ TestXmlMessage copy = rdr.Merge(TestXmlMessage.CreateBuilder(), registry).Build();
+ Assert.AreEqual(message, copy);
+ }
+ }
+}
diff --git a/src/ProtocolBuffers/CodedOutputStream.cs b/src/ProtocolBuffers/CodedOutputStream.cs
index f16abf65..25fc0eaa 100644
--- a/src/ProtocolBuffers/CodedOutputStream.cs
+++ b/src/ProtocolBuffers/CodedOutputStream.cs
@@ -127,6 +127,36 @@ namespace Google.ProtocolBuffers
#endregion
+ #region Writing of unknown fields
+
+ [Obsolete]
+ public void WriteUnknownGroup(int fieldNumber, IMessageLite value)
+ {
+ WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
+ value.WriteTo(this);
+ WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
+ }
+
+ public void WriteUnknownBytes(int fieldNumber, ByteString value)
+ {
+ WriteBytes(fieldNumber, null /*not used*/, value);
+ }
+
+ [CLSCompliant(false)]
+ public void WriteUnknownField(int fieldNumber, WireFormat.WireType wireType, ulong value)
+ {
+ if(wireType == WireFormat.WireType.Varint)
+ WriteUInt64(fieldNumber, null /*not used*/, value);
+ else if (wireType == WireFormat.WireType.Fixed32)
+ WriteFixed32(fieldNumber, null /*not used*/, (uint)value);
+ else if (wireType == WireFormat.WireType.Fixed64)
+ WriteFixed64(fieldNumber, null /*not used*/, value);
+ else
+ throw InvalidProtocolBufferException.InvalidWireType();
+ }
+
+ #endregion
+
#region Writing of tags and fields
public void WriteField(FieldType fieldType, int fieldNumber, string fieldName, object value)
@@ -308,14 +338,6 @@ namespace Google.ProtocolBuffers
WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
}
- [Obsolete]
- public void WriteUnknownGroup(int fieldNumber, string fieldName, IMessageLite value)
- {
- WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
- value.WriteTo(this);
- WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
- }
-
public void WriteMessage(int fieldNumber, string fieldName, IMessageLite value)
{
WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
diff --git a/src/ProtocolBuffers/ICodedOutputStream.cs b/src/ProtocolBuffers/ICodedOutputStream.cs
index 56ee5b61..94aeff0b 100644
--- a/src/ProtocolBuffers/ICodedOutputStream.cs
+++ b/src/ProtocolBuffers/ICodedOutputStream.cs
@@ -12,7 +12,11 @@ namespace Google.ProtocolBuffers
void Flush();
[Obsolete]
- void WriteUnknownGroup(int fieldNumber, string fieldName, IMessageLite value);
+ void WriteUnknownGroup(int fieldNumber, IMessageLite value);
+ void WriteUnknownBytes(int fieldNumber, ByteString value);
+ [CLSCompliant(false)]
+ void WriteUnknownField(int fieldNumber, WireFormat.WireType wireType, ulong value);
+
void WriteMessageSetExtension(int fieldNumber, string fieldName, IMessageLite value);
void WriteMessageSetExtension(int fieldNumber, string fieldName, ByteString value);
diff --git a/src/ProtocolBuffers/ProtocolBuffers.csproj b/src/ProtocolBuffers/ProtocolBuffers.csproj
index 336b387f..73219420 100644
--- a/src/ProtocolBuffers/ProtocolBuffers.csproj
+++ b/src/ProtocolBuffers/ProtocolBuffers.csproj
@@ -180,6 +180,14 @@
<Compile Include="NameHelpers.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RpcUtil.cs" />
+ <Compile Include="Serialization\AbstractReader.cs" />
+ <Compile Include="Serialization\AbstractTextReader.cs" />
+ <Compile Include="Serialization\AbstractTextWriter.cs" />
+ <Compile Include="Serialization\AbstractWriter.cs" />
+ <Compile Include="Serialization\XmlFormatReader.cs" />
+ <Compile Include="Serialization\XmlFormatWriter.cs" />
+ <Compile Include="Serialization\XmlReaderOptions.cs" />
+ <Compile Include="Serialization\XmlWriterOptions.cs" />
<Compile Include="SilverlightCompatibility.cs" />
<Compile Include="SortedList.cs" />
<Compile Include="TextFormat.cs" />
diff --git a/src/ProtocolBuffers/Serialization/AbstractReader.cs b/src/ProtocolBuffers/Serialization/AbstractReader.cs
new file mode 100644
index 00000000..8e4030e6
--- /dev/null
+++ b/src/ProtocolBuffers/Serialization/AbstractReader.cs
@@ -0,0 +1,508 @@
+using System;
+using System.Collections.Generic;
+using Google.ProtocolBuffers.Descriptors;
+
+//Disable CS3011: only CLS-compliant members can be abstract
+#pragma warning disable 3011
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Provides a base-class that provides some basic functionality for handling type dispatching
+ /// </summary>
+ public abstract class AbstractReader : ICodedInputStream
+ {
+ /// <summary>
+ /// Merges the contents of stream into the provided message builder
+ /// </summary>
+ public TBuilder Merge<TBuilder>(TBuilder builder) where TBuilder : IBuilderLite
+ { return Merge(builder, ExtensionRegistry.Empty); }
+
+ /// <summary>
+ /// Merges the contents of stream into the provided message builder
+ /// </summary>
+ public abstract TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry) where TBuilder : IBuilderLite;
+
+ /// <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 abstract bool PeekNext(out string field);
+
+ /// <summary>
+ /// Causes the reader to skip past this field
+ /// </summary>
+ protected abstract void Skip();
+
+ /// <summary>
+ /// Returns true if it was able to read a Boolean from the input
+ /// </summary>
+ protected abstract bool Read(ref bool value);
+
+ /// <summary>
+ /// Returns true if it was able to read a Int32 from the input
+ /// </summary>
+ protected abstract bool Read(ref int value);
+
+ /// <summary>
+ /// Returns true if it was able to read a UInt32 from the input
+ /// </summary>
+ [CLSCompliant(false)]
+ protected abstract bool Read(ref uint value);
+
+ /// <summary>
+ /// Returns true if it was able to read a Int64 from the input
+ /// </summary>
+ protected abstract bool Read(ref long value);
+
+ /// <summary>
+ /// Returns true if it was able to read a UInt64 from the input
+ /// </summary>
+ [CLSCompliant(false)]
+ protected abstract bool Read(ref ulong value);
+
+ /// <summary>
+ /// Returns true if it was able to read a Single from the input
+ /// </summary>
+ protected abstract bool Read(ref float value);
+
+ /// <summary>
+ /// Returns true if it was able to read a Double from the input
+ /// </summary>
+ protected abstract bool Read(ref double value);
+
+ /// <summary>
+ /// Returns true if it was able to read a String from the input
+ /// </summary>
+ protected abstract bool Read(ref string value);
+
+ /// <summary>
+ /// Returns true if it was able to read a ByteString from the input
+ /// </summary>
+ protected abstract bool Read(ref ByteString value);
+
+ /// <summary>
+ /// returns true if it was able to read a single value into the value reference. The value
+ /// stored may be of type System.String, System.Int32, or an IEnumLite from the IEnumLiteMap.
+ /// </summary>
+ protected abstract bool ReadEnum(ref object value);
+
+ /// <summary>
+ /// Merges the input stream into the provided IBuilderLite
+ /// </summary>
+ protected abstract bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry);
+
+ /// <summary>
+ /// Merges the input stream into the provided IBuilderLite
+ /// </summary>
+ public virtual bool ReadGroup(IBuilderLite value, ExtensionRegistry registry)
+ {
+ return ReadMessage(value, registry);
+ }
+
+ /// <summary>
+ /// Cursors through the array elements and stops at the end of the array
+ /// </summary>
+ protected virtual IEnumerable<string> ForeachArrayItem(string field)
+ {
+ string next = field;
+ while (true)
+ {
+ yield return next;
+
+ if (!PeekNext(out next) || next != field)
+ break;
+ }
+ }
+
+ /// <summary>
+ /// Reads an array of T messages
+ /// </summary>
+ public virtual bool ReadMessageArray<T>(string field, ICollection<T> items, IMessageLite messageType, ExtensionRegistry registry)
+ {
+ bool success = false;
+ foreach (string next in ForeachArrayItem(field))
+ {
+ IBuilderLite builder = messageType.WeakCreateBuilderForType();
+ if (ReadMessage(builder, registry))
+ {
+ items.Add((T)builder.WeakBuild());
+ success |= true;
+ }
+ }
+ return success;
+ }
+
+ /// <summary>
+ /// Reads an array of T messages as a proto-buffer group
+ /// </summary>
+ public virtual bool ReadGroupArray<T>(string field, ICollection<T> items, IMessageLite messageType, ExtensionRegistry registry)
+ {
+ bool success = false;
+ foreach (string next in ForeachArrayItem(field))
+ {
+ IBuilderLite builder = messageType.WeakCreateBuilderForType();
+ if (ReadGroup(builder, registry))
+ {
+ items.Add((T)builder.WeakBuild());
+ success |= true;
+ }
+ }
+ return success;
+ }
+
+ /// <summary>
+ /// Reads an array of System.Enum type T and adds them to the collection
+ /// </summary>
+ public virtual bool ReadEnumArray(string field, ICollection<object> items)
+ {
+ bool success = false;
+ foreach (string next in ForeachArrayItem(field))
+ {
+ object temp = null;
+ if (ReadEnum(ref temp))
+ {
+ items.Add(temp);
+ success |= true;
+ }
+ }
+ return success;
+ }
+
+ /// <summary>
+ /// Reads an array of T, where T is a primitive type defined by FieldType
+ /// </summary>
+ public virtual bool ReadArray<T>(FieldType type, string field, ICollection<T> items)
+ {
+ bool success = false;
+ foreach (string next in ForeachArrayItem(field))
+ {
+ object temp = null;
+ if (ReadField(type, ref temp))
+ {
+ items.Add((T)temp);
+ success |= true;
+ }
+ }
+ return success;
+ }
+
+ /// <summary>
+ /// returns true if it was able to read a single primitive value of FieldType into the value reference
+ /// </summary>
+ public virtual bool ReadField(FieldType type, ref object value)
+ {
+ switch (type)
+ {
+ case FieldType.Bool:
+ {
+ bool temp = false;
+ if (Read(ref temp))
+ value = temp;
+ else
+ return false;
+ break;
+ }
+ case FieldType.Int64:
+ case FieldType.SInt64:
+ case FieldType.SFixed64:
+ {
+ long temp = 0;
+ if (Read(ref temp))
+ value = temp;
+ else
+ return false;
+ break;
+ }
+ case FieldType.UInt64:
+ case FieldType.Fixed64:
+ {
+ ulong temp = 0;
+ if (Read(ref temp))
+ value = temp;
+ else
+ return false;
+ break;
+ }
+ case FieldType.Int32:
+ case FieldType.SInt32:
+ case FieldType.SFixed32:
+ {
+ int temp = 0;
+ if (Read(ref temp))
+ value = temp;
+ else
+ return false;
+ break;
+ }
+ case FieldType.UInt32:
+ case FieldType.Fixed32:
+ {
+ uint temp = 0;
+ if (Read(ref temp))
+ value = temp;
+ else
+ return false;
+ break;
+ }
+ case FieldType.Float:
+ {
+ float temp = float.NaN;
+ if (Read(ref temp))
+ value = temp;
+ else
+ return false;
+ break;
+ }
+ case FieldType.Double:
+ {
+ double temp = float.NaN;
+ if (Read(ref temp))
+ value = temp;
+ else
+ return false;
+ break;
+ }
+ case FieldType.String:
+ {
+ string temp = null;
+ if (Read(ref temp))
+ value = temp;
+ else
+ return false;
+ break;
+ }
+ case FieldType.Bytes:
+ {
+ ByteString temp = null;
+ if (Read(ref temp))
+ value = temp;
+ else
+ return false;
+ break;
+ }
+ default:
+ throw InvalidProtocolBufferException.InvalidTag();
+ }
+ return true;
+ }
+
+ #region ICodedInputStream Members
+
+ bool ICodedInputStream.ReadTag(out uint fieldTag, out string fieldName)
+ {
+ fieldTag = 0;
+ if (PeekNext(out fieldName))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ bool ICodedInputStream.ReadDouble(ref double value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadFloat(ref float value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadUInt64(ref ulong value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadInt64(ref long value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadInt32(ref int value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadFixed64(ref ulong value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadFixed32(ref uint value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadBool(ref bool value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadString(ref string value)
+ { return Read(ref value); }
+
+ void ICodedInputStream.ReadGroup(int fieldNumber, IBuilderLite builder, ExtensionRegistry extensionRegistry)
+ { ReadGroup(builder, extensionRegistry); }
+
+ void ICodedInputStream.ReadUnknownGroup(int fieldNumber, IBuilderLite builder)
+ { throw new NotSupportedException(); }
+
+ void ICodedInputStream.ReadMessage(IBuilderLite builder, ExtensionRegistry extensionRegistry)
+ { ReadMessage(builder, extensionRegistry); }
+
+ bool ICodedInputStream.ReadBytes(ref ByteString value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadUInt32(ref uint value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadEnum(ref IEnumLite value, out object unknown, IEnumLiteMap mapping)
+ {
+ value = null;
+ unknown = null;
+ if(ReadEnum(ref unknown))
+ {
+ if (unknown is int) value = mapping.FindValueByNumber((int)unknown);
+ else if (unknown is string) value = mapping.FindValueByName((string)unknown);
+ return value != null;
+ }
+ return false;
+ }
+
+ bool ICodedInputStream.ReadEnum<T>(ref T value, out object rawValue)
+ {
+ rawValue = null;
+ if (ReadEnum(ref rawValue))
+ {
+ if (Enum.IsDefined(typeof(T), rawValue))
+ {
+ if (rawValue is int)
+ value = (T)rawValue;
+ else if (rawValue is string)
+ value = (T)Enum.Parse(typeof(T), (string)rawValue, false);
+ else
+ {
+ value = default(T);
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool ICodedInputStream.ReadSFixed32(ref int value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadSFixed64(ref long value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadSInt32(ref int value)
+ { return Read(ref value); }
+
+ bool ICodedInputStream.ReadSInt64(ref long value)
+ { return Read(ref value); }
+
+ void ICodedInputStream.ReadPrimitiveArray(FieldType fieldType, uint fieldTag, string fieldName, ICollection<object> list)
+ { ReadArray(fieldType, fieldName, list); }
+
+ void ICodedInputStream.ReadEnumArray(uint fieldTag, string fieldName, ICollection<IEnumLite> list, out ICollection<object> unknown, IEnumLiteMap mapping)
+ {
+ unknown = null;
+ List<object> array = new List<object>();
+ if (ReadEnumArray(fieldName, array))
+ {
+ foreach (object rawValue in array)
+ {
+ IEnumLite item = null;
+ if (rawValue is int) item = mapping.FindValueByNumber((int)rawValue);
+ else if (rawValue is string) item = mapping.FindValueByName((string)rawValue);
+
+ if (item != null)
+ list.Add(item);
+ else
+ {
+ if (unknown == null) unknown = new List<object>();
+ unknown.Add(rawValue);
+ }
+ }
+ }
+ }
+
+ void ICodedInputStream.ReadEnumArray<T>(uint fieldTag, string fieldName, ICollection<T> list, out ICollection<object> unknown)
+ {
+ unknown = null;
+ List<object> array = new List<object>();
+ if (ReadEnumArray(fieldName, array))
+ {
+ foreach (object rawValue in array)
+ {
+ if (rawValue is int)
+ list.Add((T)rawValue);
+ else if (rawValue is string)
+ list.Add((T)Enum.Parse(typeof(T), (string)rawValue, false));
+ else
+ {
+ if (unknown == null) unknown = new List<object>();
+ unknown.Add(rawValue);
+ }
+ }
+ }
+ }
+
+ void ICodedInputStream.ReadMessageArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType, ExtensionRegistry registry)
+ { ReadMessageArray(fieldName, list, messageType, registry); }
+
+ void ICodedInputStream.ReadGroupArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType, ExtensionRegistry registry)
+ { ReadGroupArray(fieldName, list, messageType, registry); }
+
+ bool ICodedInputStream.ReadPrimitiveField(FieldType fieldType, ref object value)
+ { return ReadField(fieldType, ref value); }
+
+ bool ICodedInputStream.IsAtEnd
+ {
+ get { string next; return PeekNext(out next) == false; }
+ }
+
+ bool ICodedInputStream.SkipField()
+ {
+ Skip();
+ return true;
+ }
+
+ void ICodedInputStream.ReadStringArray(uint fieldTag, string fieldName, ICollection<string> list)
+ { ReadArray(FieldType.String, fieldName, list); }
+
+ void ICodedInputStream.ReadBytesArray(uint fieldTag, string fieldName, ICollection<ByteString> list)
+ { ReadArray(FieldType.Bytes, fieldName, list); }
+
+ void ICodedInputStream.ReadBoolArray(uint fieldTag, string fieldName, ICollection<bool> list)
+ { ReadArray(FieldType.Bool, fieldName, list); }
+
+ void ICodedInputStream.ReadInt32Array(uint fieldTag, string fieldName, ICollection<int> list)
+ { ReadArray(FieldType.Int32, fieldName, list); }
+
+ void ICodedInputStream.ReadSInt32Array(uint fieldTag, string fieldName, ICollection<int> list)
+ { ReadArray(FieldType.SInt32, fieldName, list); }
+
+ void ICodedInputStream.ReadUInt32Array(uint fieldTag, string fieldName, ICollection<uint> list)
+ { ReadArray(FieldType.UInt32, fieldName, list); }
+
+ void ICodedInputStream.ReadFixed32Array(uint fieldTag, string fieldName, ICollection<uint> list)
+ { ReadArray(FieldType.Fixed32, fieldName, list); }
+
+ void ICodedInputStream.ReadSFixed32Array(uint fieldTag, string fieldName, ICollection<int> list)
+ { ReadArray(FieldType.SFixed32, fieldName, list); }
+
+ void ICodedInputStream.ReadInt64Array(uint fieldTag, string fieldName, ICollection<long> list)
+ { ReadArray(FieldType.Int64, fieldName, list); }
+
+ void ICodedInputStream.ReadSInt64Array(uint fieldTag, string fieldName, ICollection<long> list)
+ { ReadArray(FieldType.SInt64, fieldName, list); }
+
+ void ICodedInputStream.ReadUInt64Array(uint fieldTag, string fieldName, ICollection<ulong> list)
+ { ReadArray(FieldType.UInt64, fieldName, list); }
+
+ void ICodedInputStream.ReadFixed64Array(uint fieldTag, string fieldName, ICollection<ulong> list)
+ { ReadArray(FieldType.Fixed64, fieldName, list); }
+
+ void ICodedInputStream.ReadSFixed64Array(uint fieldTag, string fieldName, ICollection<long> list)
+ { ReadArray(FieldType.SFixed64, fieldName, list); }
+
+ void ICodedInputStream.ReadDoubleArray(uint fieldTag, string fieldName, ICollection<double> list)
+ { ReadArray(FieldType.Double, fieldName, list); }
+
+ void ICodedInputStream.ReadFloatArray(uint fieldTag, string fieldName, ICollection<float> list)
+ { ReadArray(FieldType.Float, fieldName, list); }
+
+ #endregion
+ }
+}
diff --git a/src/ProtocolBuffers/Serialization/AbstractTextReader.cs b/src/ProtocolBuffers/Serialization/AbstractTextReader.cs
new file mode 100644
index 00000000..2f07ac72
--- /dev/null
+++ b/src/ProtocolBuffers/Serialization/AbstractTextReader.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Globalization;
+using System.Xml;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Provides a base class for text-parsing readers
+ /// </summary>
+ public abstract class AbstractTextReader : AbstractReader
+ {
+ /// <summary>
+ /// Reads a typed field as a string
+ /// </summary>
+ protected abstract bool ReadAsText(ref string textValue, Type type);
+
+ /// <summary>
+ /// Returns true if it was able to read a String from the input
+ /// </summary>
+ protected override bool Read(ref string value)
+ {
+ string text = null;
+ if (ReadAsText(ref text, typeof(string)))
+ {
+ value = text;
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if it was able to read a Boolean from the input
+ /// </summary>
+ protected override bool Read(ref bool value)
+ {
+ string text = null;
+ if (ReadAsText(ref text, typeof(bool)))
+ {
+ value = XmlConvert.ToBoolean(text);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if it was able to read a Int32 from the input
+ /// </summary>
+ protected override bool Read(ref int value)
+ {
+ string text = null;
+ if (ReadAsText(ref text, typeof(int)))
+ {
+ value = XmlConvert.ToInt32(text);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if it was able to read a UInt32 from the input
+ /// </summary>
+ [CLSCompliant(false)]
+ protected override bool Read(ref uint value)
+ {
+ string text = null;
+ if (ReadAsText(ref text, typeof(uint)))
+ {
+ value = XmlConvert.ToUInt32(text);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if it was able to read a Int64 from the input
+ /// </summary>
+ protected override bool Read(ref long value)
+ {
+ string text = null;
+ if (ReadAsText(ref text, typeof(long)))
+ {
+ value = XmlConvert.ToInt64(text);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if it was able to read a UInt64 from the input
+ /// </summary>
+ [CLSCompliant(false)]
+ protected override bool Read(ref ulong value)
+ {
+ string text = null;
+ if (ReadAsText(ref text, typeof(ulong)))
+ {
+ value = XmlConvert.ToUInt64(text);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if it was able to read a Single from the input
+ /// </summary>
+ protected override bool Read(ref float value)
+ {
+ string text = null;
+ if (ReadAsText(ref text, typeof(float)))
+ {
+ value = XmlConvert.ToSingle(text);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if it was able to read a Double from the input
+ /// </summary>
+ protected override bool Read(ref double value)
+ {
+ string text = null;
+ if (ReadAsText(ref text, typeof(double)))
+ {
+ value = XmlConvert.ToDouble(text);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Provides decoding of bytes read from the input stream
+ /// </summary>
+ protected virtual ByteString DecodeBytes(string bytes) { return ByteString.FromBase64(bytes); }
+
+ /// <summary>
+ /// Returns true if it was able to read a ByteString from the input
+ /// </summary>
+ protected override bool Read(ref ByteString value)
+ {
+ string text = null;
+ if (ReadAsText(ref text, typeof(ByteString)))
+ {
+ value = DecodeBytes(text);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// returns true if it was able to read a single value into the value reference. The value
+ /// stored may be of type System.String, System.Int32, or an IEnumLite from the IEnumLiteMap.
+ /// </summary>
+ protected override bool ReadEnum(ref object value)
+ {
+ string text = null;
+ if (ReadAsText(ref text, typeof(Enum)))
+ {
+ int number;
+ if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out number))
+ {
+ value = number;
+ return true;
+ }
+ value = text;
+ return true;
+ }
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/ProtocolBuffers/Serialization/AbstractTextWriter.cs b/src/ProtocolBuffers/Serialization/AbstractTextWriter.cs
new file mode 100644
index 00000000..75acc0f4
--- /dev/null
+++ b/src/ProtocolBuffers/Serialization/AbstractTextWriter.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Xml;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Provides a base class for text writers
+ /// </summary>
+ public abstract class AbstractTextWriter : AbstractWriter
+ {
+ /// <summary>
+ /// Encodes raw bytes to be written to the stream
+ /// </summary>
+ protected virtual string EncodeBytes(ByteString bytes) { return bytes.ToBase64(); }
+
+ /// <summary>
+ /// Writes a typed field as a text value
+ /// </summary>
+ protected abstract void WriteAsText(string field, string textValue, object typedValue);
+
+ /// <summary>
+ /// Writes a String value
+ /// </summary>
+ protected override void Write(string field, string value) { WriteAsText(field, value, value); }
+
+ /// <summary>
+ /// Writes a Boolean value
+ /// </summary>
+ protected override void Write(string field, bool value) { WriteAsText(field, XmlConvert.ToString(value), value); }
+
+ /// <summary>
+ /// Writes a Int32 value
+ /// </summary>
+ protected override void Write(string field, int value) { WriteAsText(field, XmlConvert.ToString(value), value); }
+
+ /// <summary>
+ /// Writes a UInt32 value
+ /// </summary>
+ [CLSCompliant(false)]
+ protected override void Write(string field, uint value) { WriteAsText(field, XmlConvert.ToString(value), value); }
+
+ /// <summary>
+ /// Writes a Int64 value
+ /// </summary>
+ protected override void Write(string field, long value) { WriteAsText(field, XmlConvert.ToString(value), value); }
+
+ /// <summary>
+ /// Writes a UInt64 value
+ /// </summary>
+ [CLSCompliant(false)]
+ protected override void Write(string field, ulong value) { WriteAsText(field, XmlConvert.ToString(value), value); }
+
+ /// <summary>
+ /// Writes a Single value
+ /// </summary>
+ protected override void Write(string field, float value) { WriteAsText(field, XmlConvert.ToString(value), value); }
+
+ /// <summary>
+ /// Writes a Double value
+ /// </summary>
+ protected override void Write(string field, double value) { WriteAsText(field, XmlConvert.ToString(value), value); }
+
+ /// <summary>
+ /// Writes a set of bytes
+ /// </summary>
+ protected override void Write(string field, ByteString value) { WriteAsText(field, EncodeBytes(value), value); }
+
+ /// <summary>
+ /// Writes a System.Enum by the numeric and textual value
+ /// </summary>
+ protected override void WriteEnum(string field, int number, string name) { WriteAsText(field, name, number); }
+ }
+} \ No newline at end of file
diff --git a/src/ProtocolBuffers/Serialization/AbstractWriter.cs b/src/ProtocolBuffers/Serialization/AbstractWriter.cs
new file mode 100644
index 00000000..d8a0c0a1
--- /dev/null
+++ b/src/ProtocolBuffers/Serialization/AbstractWriter.cs
@@ -0,0 +1,345 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using Google.ProtocolBuffers.Descriptors;
+
+//Disable CS3011: only CLS-compliant members can be abstract
+#pragma warning disable 3011
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Provides a base class for writers that performs some basic type dispatching
+ /// </summary>
+ public abstract class AbstractWriter : ICodedOutputStream, IDisposable
+ {
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ Flush();
+ Dispose(true);
+ }
+
+ /// <summary>
+ /// Completes any pending write operations
+ /// </summary>
+ public virtual void Flush()
+ { }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ protected virtual void Dispose(bool disposing)
+ {
+ }
+
+ /// <summary>
+ /// Writes the message to the the formatted stream.
+ /// </summary>
+ public abstract void WriteMessage(IMessageLite message);
+
+ /// <summary>
+ /// Writes a message
+ /// </summary>
+ public abstract void WriteMessage(string field, IMessageLite message);
+
+
+
+ /// <summary>
+ /// Writes a Boolean value
+ /// </summary>
+ protected abstract void Write(string field, Boolean value);
+
+ /// <summary>
+ /// Writes a Int32 value
+ /// </summary>
+ protected abstract void Write(string field, Int32 value);
+
+ /// <summary>
+ /// Writes a UInt32 value
+ /// </summary>
+ [CLSCompliant(false)]
+ protected abstract void Write(string field, UInt32 value);
+
+ /// <summary>
+ /// Writes a Int64 value
+ /// </summary>
+ protected abstract void Write(string field, Int64 value);
+
+ /// <summary>
+ /// Writes a UInt64 value
+ /// </summary>
+ [CLSCompliant(false)]
+ protected abstract void Write(string field, UInt64 value);
+
+ /// <summary>
+ /// Writes a Single value
+ /// </summary>
+ protected abstract void Write(string field, Single value);
+
+ /// <summary>
+ /// Writes a Double value
+ /// </summary>
+ protected abstract void Write(string field, Double value);
+
+ /// <summary>
+ /// Writes a String value
+ /// </summary>
+ protected abstract void Write(string field, String value);
+
+ /// <summary>
+ /// Writes a set of bytes
+ /// </summary>
+ protected abstract void Write(string field, ByteString value);
+
+ /// <summary>
+ /// Writes a message or group as a field
+ /// </summary>
+ protected abstract void WriteMessageOrGroup(string field, IMessageLite message);
+
+ /// <summary>
+ /// Writes a System.Enum by the numeric and textual value
+ /// </summary>
+ protected abstract void WriteEnum(string field, int number, string name);
+
+ /// <summary>
+ /// Writes a field of the type determined by field.FieldType
+ /// </summary>
+ protected virtual void WriteField(FieldType fieldType, string field, object value)
+ {
+ switch (fieldType)
+ {
+ case FieldType.Bool: Write(field, (bool)value); break;
+ case FieldType.Int64:
+ case FieldType.SInt64:
+ case FieldType.SFixed64: Write(field, (long)value); break;
+ case FieldType.UInt64:
+ case FieldType.Fixed64: Write(field, (ulong)value); break;
+ case FieldType.Int32:
+ case FieldType.SInt32:
+ case FieldType.SFixed32: Write(field, (int)value); break;
+ case FieldType.UInt32:
+ case FieldType.Fixed32: Write(field, (uint)value); break;
+ case FieldType.Float: Write(field, (float)value); break;
+ case FieldType.Double: Write(field, (double)value); break;
+ case FieldType.String: Write(field, (string)value); break;
+ case FieldType.Bytes: Write(field, (ByteString)value); break;
+ case FieldType.Group: WriteMessageOrGroup(field, (IMessageLite)value); break;
+ case FieldType.Message: WriteMessageOrGroup(field, (IMessageLite)value); break;
+ case FieldType.Enum:
+ {
+ if (value is IEnumLite) WriteEnum(field, ((IEnumLite)value).Number, ((IEnumLite)value).Name);
+ else if (value is IConvertible) WriteEnum(field, ((IConvertible)value).ToInt32(CultureInfo.InvariantCulture), ((IConvertible)value).ToString(CultureInfo.InvariantCulture));
+ else throw new ArgumentException("Expected an Enum type for field " + field);
+ break;
+ }
+ default:
+ throw InvalidProtocolBufferException.InvalidTag();
+ }
+ }
+
+ /// <summary>
+ /// Writes an array of field values
+ /// </summary>
+ protected virtual void WriteArray(FieldType fieldType, string field, IEnumerable items)
+ {
+ foreach (object obj in items)
+ WriteField(fieldType, field, obj);
+ }
+
+ /// <summary>
+ /// Writes a numeric unknown field of wire type: Fixed32, Fixed64, or Variant
+ /// </summary>
+ [CLSCompliant(false)]
+ protected virtual void WriteUnknown(WireFormat.WireType wireType, int fieldNumber, ulong value)
+ { }
+
+ /// <summary>
+ /// Writes an unknown field, Expect WireType of GroupStart or LengthPrefix
+ /// </summary>
+ [CLSCompliant(false)]
+ protected virtual void WriteUnknown(WireFormat.WireType wireType, int fieldNumber, ByteString value)
+ { }
+
+ #region ICodedOutputStream Members
+
+ void ICodedOutputStream.WriteUnknownGroup(int fieldNumber, IMessageLite value)
+ { }
+ void ICodedOutputStream.WriteUnknownBytes(int fieldNumber, ByteString value)
+ { }
+ void ICodedOutputStream.WriteUnknownField(int fieldNumber, WireFormat.WireType type, ulong value)
+ { }
+
+ void ICodedOutputStream.WriteMessageSetExtension(int fieldNumber, string fieldName, IMessageLite value)
+ { }
+
+ void ICodedOutputStream.WriteMessageSetExtension(int fieldNumber, string fieldName, ByteString value)
+ { }
+
+ void ICodedOutputStream.WriteField(FieldType fieldType, int fieldNumber, string fieldName, object value)
+ { WriteField(fieldType, fieldName, value); }
+
+ void ICodedOutputStream.WriteDouble(int fieldNumber, string fieldName, double value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteFloat(int fieldNumber, string fieldName, float value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteUInt64(int fieldNumber, string fieldName, ulong value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteInt64(int fieldNumber, string fieldName, long value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteInt32(int fieldNumber, string fieldName, int value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteFixed64(int fieldNumber, string fieldName, ulong value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteFixed32(int fieldNumber, string fieldName, uint value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteBool(int fieldNumber, string fieldName, bool value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteString(int fieldNumber, string fieldName, string value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteGroup(int fieldNumber, string fieldName, IMessageLite value)
+ { WriteMessageOrGroup(fieldName, value); }
+
+ void ICodedOutputStream.WriteMessage(int fieldNumber, string fieldName, IMessageLite value)
+ { WriteMessageOrGroup(fieldName, value); }
+
+ void ICodedOutputStream.WriteBytes(int fieldNumber, string fieldName, ByteString value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteUInt32(int fieldNumber, string fieldName, uint value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteEnum(int fieldNumber, string fieldName, int value, object rawValue)
+ { WriteEnum(fieldName, value, rawValue.ToString()); }
+
+ void ICodedOutputStream.WriteSFixed32(int fieldNumber, string fieldName, int value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteSFixed64(int fieldNumber, string fieldName, long value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteSInt32(int fieldNumber, string fieldName, int value)
+ { Write(fieldName, value); }
+
+ void ICodedOutputStream.WriteSInt64(int fieldNumber, string fieldName, long value)
+ { Write(fieldName, value); }
+
+
+ void ICodedOutputStream.WriteArray(FieldType fieldType, int fieldNumber, string fieldName, IEnumerable list)
+ { WriteArray(fieldType, fieldName, list); }
+
+ void ICodedOutputStream.WriteGroupArray<T>(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<T> list)
+ { WriteArray(FieldType.Group, fieldName, list); }
+
+ void ICodedOutputStream.WriteMessageArray<T>(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<T> list)
+ { WriteArray(FieldType.Message, fieldName, list); }
+
+ void ICodedOutputStream.WriteStringArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<string> list)
+ { WriteArray(FieldType.String, fieldName, list); }
+
+ void ICodedOutputStream.WriteBytesArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<ByteString> list)
+ { WriteArray(FieldType.Bytes, fieldName, list); }
+
+ void ICodedOutputStream.WriteBoolArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<bool> list)
+ { WriteArray(FieldType.Bool, fieldName, list); }
+
+ void ICodedOutputStream.WriteInt32Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<int> list)
+ { WriteArray(FieldType.Int32, fieldName, list); }
+
+ void ICodedOutputStream.WriteSInt32Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<int> list)
+ { WriteArray(FieldType.SInt32, fieldName, list); }
+
+ void ICodedOutputStream.WriteUInt32Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<uint> list)
+ { WriteArray(FieldType.UInt32, fieldName, list); }
+
+ void ICodedOutputStream.WriteFixed32Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<uint> list)
+ { WriteArray(FieldType.Fixed32, fieldName, list); }
+
+ void ICodedOutputStream.WriteSFixed32Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<int> list)
+ { WriteArray(FieldType.SFixed32, fieldName, list); }
+
+ void ICodedOutputStream.WriteInt64Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<long> list)
+ { WriteArray(FieldType.Int64, fieldName, list); }
+
+ void ICodedOutputStream.WriteSInt64Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<long> list)
+ { WriteArray(FieldType.SInt64, fieldName, list); }
+
+ void ICodedOutputStream.WriteUInt64Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<ulong> list)
+ { WriteArray(FieldType.UInt64, fieldName, list); }
+
+ void ICodedOutputStream.WriteFixed64Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<ulong> list)
+ { WriteArray(FieldType.Fixed64, fieldName, list); }
+
+ void ICodedOutputStream.WriteSFixed64Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<long> list)
+ { WriteArray(FieldType.SFixed64, fieldName, list); }
+
+ void ICodedOutputStream.WriteDoubleArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<double> list)
+ { WriteArray(FieldType.Double, fieldName, list); }
+
+ void ICodedOutputStream.WriteFloatArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<float> list)
+ { WriteArray(FieldType.Float, fieldName, list); }
+
+ void ICodedOutputStream.WriteEnumArray<T>(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable<T> list)
+ { WriteArray(FieldType.Enum, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedArray(FieldType fieldType, int fieldNumber, string fieldName, IEnumerable list)
+ { WriteArray(fieldType, fieldName, list); }
+
+
+ void ICodedOutputStream.WritePackedBoolArray(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<bool> list)
+ { WriteArray(FieldType.Bool, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedInt32Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<int> list)
+ { WriteArray(FieldType.Int32, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedSInt32Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<int> list)
+ { WriteArray(FieldType.SInt32, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedUInt32Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<uint> list)
+ { WriteArray(FieldType.UInt32, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedFixed32Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<uint> list)
+ { WriteArray(FieldType.Fixed32, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedSFixed32Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<int> list)
+ { WriteArray(FieldType.SFixed32, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedInt64Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<long> list)
+ { WriteArray(FieldType.Int64, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedSInt64Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<long> list)
+ { WriteArray(FieldType.SInt64, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedUInt64Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<ulong> list)
+ { WriteArray(FieldType.UInt64, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedFixed64Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<ulong> list)
+ { WriteArray(FieldType.Fixed64, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedSFixed64Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<long> list)
+ { WriteArray(FieldType.SFixed64, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedDoubleArray(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<double> list)
+ { WriteArray(FieldType.Double, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedFloatArray(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<float> list)
+ { WriteArray(FieldType.Float, fieldName, list); }
+
+ void ICodedOutputStream.WritePackedEnumArray<T>(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable<T> list)
+ { WriteArray(FieldType.Enum, fieldName, list); }
+
+ #endregion
+ }
+}
diff --git a/src/ProtocolBuffers/Serialization/XmlFormatReader.cs b/src/ProtocolBuffers/Serialization/XmlFormatReader.cs
new file mode 100644
index 00000000..671490e6
--- /dev/null
+++ b/src/ProtocolBuffers/Serialization/XmlFormatReader.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Xml;
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Parses a proto buffer from an XML document or fragment. .NET 3.5 users may also
+ /// use this class to process Json by setting the options to support Json and providing
+ /// an XmlReader obtained from <see cref="System.Runtime.Serialization.Json.JsonReaderWriterFactory"/>.
+ /// </summary>
+ public class XmlFormatReader : AbstractTextReader
+ {
+ public const string DefaultRootElementName = XmlFormatWriter.DefaultRootElementName;
+ private readonly XmlReader _input;
+ private string _rootElementName;
+
+ static XmlReaderSettings DefaultSettings
+ {
+ get { return new XmlReaderSettings() { CheckCharacters=false, IgnoreComments=true, IgnoreProcessingInstructions = true }; }
+ }
+
+ /// <summary>
+ /// Constructs the XmlFormatReader using the stream provided as the xml
+ /// </summary>
+ public XmlFormatReader(Stream input) : this(XmlReader.Create(input, DefaultSettings)) { }
+ /// <summary>
+ /// Constructs the XmlFormatReader using the string provided as the xml to be read
+ /// </summary>
+ public XmlFormatReader(String input) : this(XmlReader.Create(new StringReader(input))) { }
+ /// <summary>
+ /// Constructs the XmlFormatReader using the xml in the TextReader
+ /// </summary>
+ public XmlFormatReader(TextReader input) : this(XmlReader.Create(input)) { }
+ /// <summary>
+ /// Constructs the XmlFormatReader with the XmlReader
+ /// </summary>
+ public XmlFormatReader(XmlReader input) : this(input, XmlReaderOptions.None) { }
+ /// <summary>
+ /// Constructs the XmlFormatReader with the XmlReader and options
+ /// </summary>
+ public XmlFormatReader(XmlReader input, XmlReaderOptions options)
+ {
+ _input = input;
+ _rootElementName = DefaultRootElementName;
+ Options = options;
+ }
+
+ /// <summary>
+ /// Gets or sets the options to use when reading the xml
+ /// </summary>
+ public XmlReaderOptions Options { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default element name to use when using the Merge&lt;TBuilder>()
+ /// </summary>
+ public string RootElementName
+ {
+ get { return _rootElementName; }
+ set { ThrowHelper.ThrowIfNull(value, "RootElementName"); _rootElementName = value; }
+ }
+
+ private XmlFormatReader CloneWith(XmlReader rdr)
+ {
+ return new XmlFormatReader(rdr, Options);
+ }
+ private void NextElement()
+ {
+ while (!_input.IsStartElement() && _input.Read())
+ continue;
+ }
+ private static void Assert(bool cond)
+ {
+ if (!cond) throw new FormatException();
+ }
+
+ /// <summary>
+ /// Merge the provided builder as an element named <see cref="RootElementName"/> in the current context
+ /// </summary>
+ public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
+ { return Merge(_rootElementName, builder, registry); }
+
+ /// <summary>
+ /// Merge the provided builder as an element of the current context
+ /// </summary>
+ public TBuilder Merge<TBuilder>(string element, TBuilder builder) where TBuilder : IBuilderLite
+ { return Merge(element, builder, ExtensionRegistry.Empty); }
+
+ /// <summary>
+ /// Merge the provided builder as an element of the current context
+ /// </summary>
+ public TBuilder Merge<TBuilder>(string element, TBuilder builder, ExtensionRegistry registry) where TBuilder : IBuilderLite
+ {
+ string field;
+ Assert(PeekNext(out field) && field == element);
+ ReadMessage(builder, registry);
+ return builder;
+ }
+
+ /// <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)
+ {
+ NextElement();
+ if(_input.IsStartElement())
+ {
+ field = _input.LocalName;
+ return true;
+ }
+ field = null;
+ return false;
+ }
+
+ /// <summary>
+ /// Causes the reader to skip past this field
+ /// </summary>
+ protected override void Skip()
+ {
+ if (_input.IsStartElement())
+ {
+ if (!_input.IsEmptyElement)
+ {
+ int depth = _input.Depth;
+ while (_input.Depth >= depth && _input.NodeType != XmlNodeType.EndElement)
+ Assert(_input.Read());
+ }
+ _input.Read();
+ }
+ }
+
+ /// <summary>
+ /// returns true if it was able to read a single value into the value reference. The value
+ /// stored may be of type System.String, System.Int32, or an IEnumLite from the IEnumLiteMap.
+ /// </summary>
+ protected override bool ReadEnum(ref object value)
+ {
+ int number;
+ string temp;
+ if (null != (temp = _input.GetAttribute("value")) && int.TryParse(temp, out number))
+ {
+ Skip();
+ value = number;
+ return true;
+ }
+ return base.ReadEnum(ref value);
+ }
+
+ /// <summary>
+ /// Returns true if it was able to read a String from the input
+ /// </summary>
+ protected override bool ReadAsText(ref string value, Type type)
+ {
+ Assert(_input.NodeType == XmlNodeType.Element);
+ value = _input.ReadElementContentAsString();
+
+ return true;
+ }
+
+ /// <summary>
+ /// Merges the input stream into the provided IBuilderLite
+ /// </summary>
+ 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();
+ return true;
+ }
+
+ private IEnumerable<string> NonNestedArrayItems(string field)
+ {
+ return base.ForeachArrayItem(field);
+ }
+
+ /// <summary>
+ /// Cursors through the array elements and stops at the end of the array
+ /// </summary>
+ protected override IEnumerable<string> ForeachArrayItem(string field)
+ {
+ bool isNested = (Options & XmlReaderOptions.ReadNestedArrays) != 0;
+
+ if (!isNested)
+ {
+ foreach (string item in NonNestedArrayItems(field))
+ yield return item;
+ yield break;
+ }
+ if (!_input.IsEmptyElement)
+ {
+ int depth = _input.Depth;
+ XmlReader child = _input.ReadSubtree();
+
+ while (!child.IsStartElement() && child.Read())
+ continue;
+ child.Read();
+
+ foreach (string item in CloneWith(child).NonNestedArrayItems("item"))
+ yield return item;
+ Assert(depth == _input.Depth && _input.NodeType == XmlNodeType.EndElement);
+ }
+ _input.Read();
+ yield break;
+ }
+ }
+}
diff --git a/src/ProtocolBuffers/Serialization/XmlFormatWriter.cs b/src/ProtocolBuffers/Serialization/XmlFormatWriter.cs
new file mode 100644
index 00000000..5ed96651
--- /dev/null
+++ b/src/ProtocolBuffers/Serialization/XmlFormatWriter.cs
@@ -0,0 +1,166 @@
+using System;
+using System.IO;
+using System.Xml;
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Writes a proto buffer to an XML document or fragment. .NET 3.5 users may also
+ /// use this class to produce Json by setting the options to support Json and providing
+ /// an XmlWriter obtained from <see cref="System.Runtime.Serialization.Json.JsonReaderWriterFactory"/>.
+ /// </summary>
+ public class XmlFormatWriter : AbstractTextWriter
+ {
+ public const string DefaultRootElementName = "root";
+ private const int NestedArrayFlag = 0x0001;
+ private readonly XmlWriter _output;
+ private string _rootElementName;
+
+ static XmlWriterSettings DefaultSettings
+ {
+ get { return new XmlWriterSettings() {CheckCharacters = false, NewLineHandling = NewLineHandling.Entitize}; }
+ }
+
+ /// <summary>
+ /// Constructs the XmlFormatWriter to write to the given TextWriter
+ /// </summary>
+ public XmlFormatWriter(TextWriter output) : this(XmlWriter.Create(output, DefaultSettings)) { }
+ /// <summary>
+ /// Constructs the XmlFormatWriter to write to the given stream
+ /// </summary>
+ public XmlFormatWriter(Stream output) : this(XmlWriter.Create(output, DefaultSettings)) { }
+ /// <summary>
+ /// Constructs the XmlFormatWriter to write to the given XmlWriter
+ /// </summary>
+ public XmlFormatWriter(XmlWriter output)
+ {
+ _output = output;
+ _rootElementName = DefaultRootElementName;
+ }
+
+ /// <summary>
+ /// Closes the underlying XmlTextWriter
+ /// </summary>
+ protected override void Dispose(bool disposing)
+ {
+ if(disposing)
+ _output.Close();
+ }
+
+ /// <summary>
+ /// Gets or sets the default element name to use when using the Merge&lt;TBuilder>()
+ /// </summary>
+ public string RootElementName
+ {
+ get { return _rootElementName; }
+ set { ThrowHelper.ThrowIfNull(value, "RootElementName"); _rootElementName = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the options to use while generating the XML
+ /// </summary>
+ public XmlWriterOptions Options { get; set; }
+
+ private bool TestOption(XmlWriterOptions option) { return (Options & option) != 0; }
+
+ /// <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 override void WriteMessage(string elementName, IMessageLite message)
+ {
+ if (TestOption(XmlWriterOptions.OutputJsonTypes))
+ {
+ _output.WriteStartElement("root"); // json requires this is the root-element
+ _output.WriteAttributeString("type", "object");
+ }
+ else
+ _output.WriteStartElement(elementName);
+
+ message.WriteTo(this);
+ _output.WriteEndElement();
+ _output.Flush();
+ }
+
+ /// <summary>
+ /// Writes a message
+ /// </summary>
+ protected override void WriteMessageOrGroup(string field, IMessageLite message)
+ {
+ _output.WriteStartElement(field);
+
+ if (TestOption(XmlWriterOptions.OutputJsonTypes))
+ _output.WriteAttributeString("type", "object");
+
+ message.WriteTo(this);
+ _output.WriteEndElement();
+ }
+
+ /// <summary>
+ /// Writes a String value
+ /// </summary>
+ protected override void WriteAsText(string field, string textValue, object typedValue)
+ {
+ _output.WriteStartElement(field);
+
+ if (TestOption(XmlWriterOptions.OutputJsonTypes))
+ {
+ if (typedValue is int || typedValue is uint || typedValue is long || typedValue is ulong || typedValue is double || typedValue is float)
+ _output.WriteAttributeString("type", "number");
+ else if (typedValue is bool)
+ _output.WriteAttributeString("type", "boolean");
+ }
+ _output.WriteString(textValue);
+
+ //Empty strings should not be written as empty elements '<item/>', rather as '<item></item>'
+ if (_output.WriteState == WriteState.Element)
+ _output.WriteRaw("");
+
+ _output.WriteEndElement();
+ }
+
+ /// <summary>
+ /// Writes an array of field values
+ /// </summary>
+ protected override void WriteArray(FieldType fieldType, string field, System.Collections.IEnumerable items)
+ {
+ //see if it's empty
+ System.Collections.IEnumerator eitems = items.GetEnumerator();
+ try { if (!eitems.MoveNext()) return; }
+ finally
+ { if (eitems is IDisposable) ((IDisposable) eitems).Dispose(); }
+
+ if (TestOption(XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputJsonTypes))
+ {
+ _output.WriteStartElement(field);
+ if (TestOption(XmlWriterOptions.OutputJsonTypes))
+ _output.WriteAttributeString("type", "array");
+
+ base.WriteArray(fieldType, "item", items);
+ _output.WriteEndElement();
+ }
+ else
+ base.WriteArray(fieldType, field, items);
+ }
+
+ /// <summary>
+ /// Writes a System.Enum by the numeric and textual value
+ /// </summary>
+ protected override void WriteEnum(string field, int number, string name)
+ {
+ _output.WriteStartElement(field);
+
+ if (!TestOption(XmlWriterOptions.OutputJsonTypes) && TestOption(XmlWriterOptions.OutputEnumValues))
+ _output.WriteAttributeString("value", XmlConvert.ToString(number));
+
+ _output.WriteString(name);
+ _output.WriteEndElement();
+ }
+ }
+}
diff --git a/src/ProtocolBuffers/Serialization/XmlReaderOptions.cs b/src/ProtocolBuffers/Serialization/XmlReaderOptions.cs
new file mode 100644
index 00000000..fc75e4b5
--- /dev/null
+++ b/src/ProtocolBuffers/Serialization/XmlReaderOptions.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Options available for the xml reader output
+ /// </summary>
+ [Flags]
+ public enum XmlReaderOptions
+ {
+ /// <summary> Simple xml formatting with no attributes </summary>
+ None,
+ /// <summary> Requires that arrays items are nested in an &lt;item> element </summary>
+ ReadNestedArrays = 1,
+ }
+} \ No newline at end of file
diff --git a/src/ProtocolBuffers/Serialization/XmlWriterOptions.cs b/src/ProtocolBuffers/Serialization/XmlWriterOptions.cs
new file mode 100644
index 00000000..2d91c742
--- /dev/null
+++ b/src/ProtocolBuffers/Serialization/XmlWriterOptions.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// Options available for the xml writer output
+ /// </summary>
+ [Flags]
+ public enum XmlWriterOptions
+ {
+ /// <summary> Simple xml formatting with no attributes </summary>
+ None,
+ /// <summary> Writes the 'value' attribute on all enumerations with the numeric identifier </summary>
+ OutputEnumValues = 0x1,
+ /// <summary> Embeds array items into child &lt;item> elements </summary>
+ OutputNestedArrays = 0x4,
+ /// <summary> Outputs the 'type' attribute for compatibility with the <see cref="System.Runtime.Serialization.Json.JsonReaderWriterFactory">JsonReaderWriterFactory</see> </summary>
+ /// <remarks> This option must, by nessessity, also enable NestedArrayItems </remarks>
+ OutputJsonTypes = 0x8,
+ }
+} \ No newline at end of file
diff --git a/src/ProtocolBuffers/UnknownField.cs b/src/ProtocolBuffers/UnknownField.cs
index edbbc29e..e03477fe 100644
--- a/src/ProtocolBuffers/UnknownField.cs
+++ b/src/ProtocolBuffers/UnknownField.cs
@@ -178,24 +178,24 @@ namespace Google.ProtocolBuffers
{
foreach (ulong value in varintList)
{
- output.WriteUInt64(fieldNumber, UnknownFieldName, value);
+ output.WriteUnknownField(fieldNumber, WireFormat.WireType.Varint, value);
}
foreach (uint value in fixed32List)
{
- output.WriteFixed32(fieldNumber, UnknownFieldName, value);
+ output.WriteUnknownField(fieldNumber, WireFormat.WireType.Fixed32, value);
}
foreach (ulong value in fixed64List)
{
- output.WriteFixed64(fieldNumber, UnknownFieldName, value);
+ output.WriteUnknownField(fieldNumber, WireFormat.WireType.Fixed64, value);
}
foreach (ByteString value in lengthDelimitedList)
{
- output.WriteBytes(fieldNumber, UnknownFieldName, value);
+ output.WriteUnknownBytes(fieldNumber, value);
}
foreach (UnknownFieldSet value in groupList)
{
#pragma warning disable 0612
- output.WriteUnknownGroup(fieldNumber, UnknownFieldName, value);
+ output.WriteUnknownGroup(fieldNumber, value);
#pragma warning restore 0612
}
}
diff --git a/src/ProtocolBuffers/UnknownFieldSet.cs b/src/ProtocolBuffers/UnknownFieldSet.cs
index fbafb892..0918503e 100644
--- a/src/ProtocolBuffers/UnknownFieldSet.cs
+++ b/src/ProtocolBuffers/UnknownFieldSet.cs
@@ -446,6 +446,12 @@ namespace Google.ProtocolBuffers
[CLSCompliant(false)]
public bool MergeFieldFrom(uint tag, ICodedInputStream input)
{
+ if (tag == 0)
+ {
+ input.SkipField();
+ return true;
+ }
+
int number = WireFormat.GetTagFieldNumber(tag);
switch (WireFormat.GetTagWireType(tag))
{