From 2b86884659413efb1cbbcf7ebe22ef46a565b13d Mon Sep 17 00:00:00 2001 From: csharptest Date: Fri, 10 Jun 2011 14:41:47 -0500 Subject: Added the XmlFormatWriter/Reader --- .../CompatTests/BinaryCompatibilityTests.cs | 2 - .../CompatTests/CompatibilityTests.cs | 1 - .../CompatTests/TextCompatibilityTests.cs | 2 - .../CompatTests/XmlCompatibilityTests.cs | 24 + .../ProtocolBuffers.Test.csproj | 2 + src/ProtocolBuffers.Test/TestWriterFormatXml.cs | 308 +++++++++++++ src/ProtocolBuffers/CodedOutputStream.cs | 38 +- src/ProtocolBuffers/ICodedOutputStream.cs | 6 +- src/ProtocolBuffers/ProtocolBuffers.csproj | 8 + .../Serialization/AbstractReader.cs | 508 +++++++++++++++++++++ .../Serialization/AbstractTextReader.cs | 171 +++++++ .../Serialization/AbstractTextWriter.cs | 73 +++ .../Serialization/AbstractWriter.cs | 345 ++++++++++++++ .../Serialization/XmlFormatReader.cs | 222 +++++++++ .../Serialization/XmlFormatWriter.cs | 166 +++++++ .../Serialization/XmlReaderOptions.cs | 16 + .../Serialization/XmlWriterOptions.cs | 21 + src/ProtocolBuffers/UnknownField.cs | 10 +- src/ProtocolBuffers/UnknownFieldSet.cs | 6 + 19 files changed, 1910 insertions(+), 19 deletions(-) create mode 100644 src/ProtocolBuffers.Test/CompatTests/XmlCompatibilityTests.cs create mode 100644 src/ProtocolBuffers.Test/TestWriterFormatXml.cs create mode 100644 src/ProtocolBuffers/Serialization/AbstractReader.cs create mode 100644 src/ProtocolBuffers/Serialization/AbstractTextReader.cs create mode 100644 src/ProtocolBuffers/Serialization/AbstractTextWriter.cs create mode 100644 src/ProtocolBuffers/Serialization/AbstractWriter.cs create mode 100644 src/ProtocolBuffers/Serialization/XmlFormatReader.cs create mode 100644 src/ProtocolBuffers/Serialization/XmlFormatWriter.cs create mode 100644 src/ProtocolBuffers/Serialization/XmlReaderOptions.cs create mode 100644 src/ProtocolBuffers/Serialization/XmlWriterOptions.cs (limited to 'src') 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 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 message) where TMessage : IMessageLite where TBuilder : IBuilderLite; 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 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 message) + { + StringWriter text = new StringWriter(); + XmlFormatWriter writer = new XmlFormatWriter(text); + writer.WriteMessage("root", message); + return text.ToString(); + } + + protected override TBuilder DeerializeMessage(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 @@ TestResources.resx + @@ -114,6 +115,7 @@ + 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("").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 @@ + + + + + + + + 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 +{ + /// + /// Provides a base-class that provides some basic functionality for handling type dispatching + /// + public abstract class AbstractReader : ICodedInputStream + { + /// + /// Merges the contents of stream into the provided message builder + /// + public TBuilder Merge(TBuilder builder) where TBuilder : IBuilderLite + { return Merge(builder, ExtensionRegistry.Empty); } + + /// + /// Merges the contents of stream into the provided message builder + /// + public abstract TBuilder Merge(TBuilder builder, ExtensionRegistry registry) where TBuilder : IBuilderLite; + + /// + /// Peeks at the next field in the input stream and returns what information is available. + /// + /// + /// 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. + /// + protected abstract bool PeekNext(out string field); + + /// + /// Causes the reader to skip past this field + /// + protected abstract void Skip(); + + /// + /// Returns true if it was able to read a Boolean from the input + /// + protected abstract bool Read(ref bool value); + + /// + /// Returns true if it was able to read a Int32 from the input + /// + protected abstract bool Read(ref int value); + + /// + /// Returns true if it was able to read a UInt32 from the input + /// + [CLSCompliant(false)] + protected abstract bool Read(ref uint value); + + /// + /// Returns true if it was able to read a Int64 from the input + /// + protected abstract bool Read(ref long value); + + /// + /// Returns true if it was able to read a UInt64 from the input + /// + [CLSCompliant(false)] + protected abstract bool Read(ref ulong value); + + /// + /// Returns true if it was able to read a Single from the input + /// + protected abstract bool Read(ref float value); + + /// + /// Returns true if it was able to read a Double from the input + /// + protected abstract bool Read(ref double value); + + /// + /// Returns true if it was able to read a String from the input + /// + protected abstract bool Read(ref string value); + + /// + /// Returns true if it was able to read a ByteString from the input + /// + protected abstract bool Read(ref ByteString value); + + /// + /// 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. + /// + protected abstract bool ReadEnum(ref object value); + + /// + /// Merges the input stream into the provided IBuilderLite + /// + protected abstract bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry); + + /// + /// Merges the input stream into the provided IBuilderLite + /// + public virtual bool ReadGroup(IBuilderLite value, ExtensionRegistry registry) + { + return ReadMessage(value, registry); + } + + /// + /// Cursors through the array elements and stops at the end of the array + /// + protected virtual IEnumerable ForeachArrayItem(string field) + { + string next = field; + while (true) + { + yield return next; + + if (!PeekNext(out next) || next != field) + break; + } + } + + /// + /// Reads an array of T messages + /// + public virtual bool ReadMessageArray(string field, ICollection 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; + } + + /// + /// Reads an array of T messages as a proto-buffer group + /// + public virtual bool ReadGroupArray(string field, ICollection 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; + } + + /// + /// Reads an array of System.Enum type T and adds them to the collection + /// + public virtual bool ReadEnumArray(string field, ICollection items) + { + bool success = false; + foreach (string next in ForeachArrayItem(field)) + { + object temp = null; + if (ReadEnum(ref temp)) + { + items.Add(temp); + success |= true; + } + } + return success; + } + + /// + /// Reads an array of T, where T is a primitive type defined by FieldType + /// + public virtual bool ReadArray(FieldType type, string field, ICollection 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; + } + + /// + /// returns true if it was able to read a single primitive value of FieldType into the value reference + /// + 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(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 list) + { ReadArray(fieldType, fieldName, list); } + + void ICodedInputStream.ReadEnumArray(uint fieldTag, string fieldName, ICollection list, out ICollection unknown, IEnumLiteMap mapping) + { + unknown = null; + List array = new List(); + 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(); + unknown.Add(rawValue); + } + } + } + } + + void ICodedInputStream.ReadEnumArray(uint fieldTag, string fieldName, ICollection list, out ICollection unknown) + { + unknown = null; + List array = new List(); + 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(); + unknown.Add(rawValue); + } + } + } + } + + void ICodedInputStream.ReadMessageArray(uint fieldTag, string fieldName, ICollection list, T messageType, ExtensionRegistry registry) + { ReadMessageArray(fieldName, list, messageType, registry); } + + void ICodedInputStream.ReadGroupArray(uint fieldTag, string fieldName, ICollection 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 list) + { ReadArray(FieldType.String, fieldName, list); } + + void ICodedInputStream.ReadBytesArray(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.Bytes, fieldName, list); } + + void ICodedInputStream.ReadBoolArray(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.Bool, fieldName, list); } + + void ICodedInputStream.ReadInt32Array(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.Int32, fieldName, list); } + + void ICodedInputStream.ReadSInt32Array(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.SInt32, fieldName, list); } + + void ICodedInputStream.ReadUInt32Array(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.UInt32, fieldName, list); } + + void ICodedInputStream.ReadFixed32Array(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.Fixed32, fieldName, list); } + + void ICodedInputStream.ReadSFixed32Array(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.SFixed32, fieldName, list); } + + void ICodedInputStream.ReadInt64Array(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.Int64, fieldName, list); } + + void ICodedInputStream.ReadSInt64Array(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.SInt64, fieldName, list); } + + void ICodedInputStream.ReadUInt64Array(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.UInt64, fieldName, list); } + + void ICodedInputStream.ReadFixed64Array(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.Fixed64, fieldName, list); } + + void ICodedInputStream.ReadSFixed64Array(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.SFixed64, fieldName, list); } + + void ICodedInputStream.ReadDoubleArray(uint fieldTag, string fieldName, ICollection list) + { ReadArray(FieldType.Double, fieldName, list); } + + void ICodedInputStream.ReadFloatArray(uint fieldTag, string fieldName, ICollection 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 +{ + /// + /// Provides a base class for text-parsing readers + /// + public abstract class AbstractTextReader : AbstractReader + { + /// + /// Reads a typed field as a string + /// + protected abstract bool ReadAsText(ref string textValue, Type type); + + /// + /// Returns true if it was able to read a String from the input + /// + protected override bool Read(ref string value) + { + string text = null; + if (ReadAsText(ref text, typeof(string))) + { + value = text; + return true; + } + return false; + } + + /// + /// Returns true if it was able to read a Boolean from the input + /// + protected override bool Read(ref bool value) + { + string text = null; + if (ReadAsText(ref text, typeof(bool))) + { + value = XmlConvert.ToBoolean(text); + return true; + } + return false; + } + + /// + /// Returns true if it was able to read a Int32 from the input + /// + protected override bool Read(ref int value) + { + string text = null; + if (ReadAsText(ref text, typeof(int))) + { + value = XmlConvert.ToInt32(text); + return true; + } + return false; + } + + /// + /// Returns true if it was able to read a UInt32 from the input + /// + [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; + } + + /// + /// Returns true if it was able to read a Int64 from the input + /// + protected override bool Read(ref long value) + { + string text = null; + if (ReadAsText(ref text, typeof(long))) + { + value = XmlConvert.ToInt64(text); + return true; + } + return false; + } + + /// + /// Returns true if it was able to read a UInt64 from the input + /// + [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; + } + + /// + /// Returns true if it was able to read a Single from the input + /// + protected override bool Read(ref float value) + { + string text = null; + if (ReadAsText(ref text, typeof(float))) + { + value = XmlConvert.ToSingle(text); + return true; + } + return false; + } + + /// + /// Returns true if it was able to read a Double from the input + /// + protected override bool Read(ref double value) + { + string text = null; + if (ReadAsText(ref text, typeof(double))) + { + value = XmlConvert.ToDouble(text); + return true; + } + return false; + } + + /// + /// Provides decoding of bytes read from the input stream + /// + protected virtual ByteString DecodeBytes(string bytes) { return ByteString.FromBase64(bytes); } + + /// + /// Returns true if it was able to read a ByteString from the input + /// + protected override bool Read(ref ByteString value) + { + string text = null; + if (ReadAsText(ref text, typeof(ByteString))) + { + value = DecodeBytes(text); + return true; + } + return false; + } + + /// + /// 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. + /// + 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 +{ + /// + /// Provides a base class for text writers + /// + public abstract class AbstractTextWriter : AbstractWriter + { + /// + /// Encodes raw bytes to be written to the stream + /// + protected virtual string EncodeBytes(ByteString bytes) { return bytes.ToBase64(); } + + /// + /// Writes a typed field as a text value + /// + protected abstract void WriteAsText(string field, string textValue, object typedValue); + + /// + /// Writes a String value + /// + protected override void Write(string field, string value) { WriteAsText(field, value, value); } + + /// + /// Writes a Boolean value + /// + protected override void Write(string field, bool value) { WriteAsText(field, XmlConvert.ToString(value), value); } + + /// + /// Writes a Int32 value + /// + protected override void Write(string field, int value) { WriteAsText(field, XmlConvert.ToString(value), value); } + + /// + /// Writes a UInt32 value + /// + [CLSCompliant(false)] + protected override void Write(string field, uint value) { WriteAsText(field, XmlConvert.ToString(value), value); } + + /// + /// Writes a Int64 value + /// + protected override void Write(string field, long value) { WriteAsText(field, XmlConvert.ToString(value), value); } + + /// + /// Writes a UInt64 value + /// + [CLSCompliant(false)] + protected override void Write(string field, ulong value) { WriteAsText(field, XmlConvert.ToString(value), value); } + + /// + /// Writes a Single value + /// + protected override void Write(string field, float value) { WriteAsText(field, XmlConvert.ToString(value), value); } + + /// + /// Writes a Double value + /// + protected override void Write(string field, double value) { WriteAsText(field, XmlConvert.ToString(value), value); } + + /// + /// Writes a set of bytes + /// + protected override void Write(string field, ByteString value) { WriteAsText(field, EncodeBytes(value), value); } + + /// + /// Writes a System.Enum by the numeric and textual value + /// + 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 +{ + /// + /// Provides a base class for writers that performs some basic type dispatching + /// + public abstract class AbstractWriter : ICodedOutputStream, IDisposable + { + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + GC.SuppressFinalize(this); + Flush(); + Dispose(true); + } + + /// + /// Completes any pending write operations + /// + public virtual void Flush() + { } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + } + + /// + /// Writes the message to the the formatted stream. + /// + public abstract void WriteMessage(IMessageLite message); + + /// + /// Writes a message + /// + public abstract void WriteMessage(string field, IMessageLite message); + + + + /// + /// Writes a Boolean value + /// + protected abstract void Write(string field, Boolean value); + + /// + /// Writes a Int32 value + /// + protected abstract void Write(string field, Int32 value); + + /// + /// Writes a UInt32 value + /// + [CLSCompliant(false)] + protected abstract void Write(string field, UInt32 value); + + /// + /// Writes a Int64 value + /// + protected abstract void Write(string field, Int64 value); + + /// + /// Writes a UInt64 value + /// + [CLSCompliant(false)] + protected abstract void Write(string field, UInt64 value); + + /// + /// Writes a Single value + /// + protected abstract void Write(string field, Single value); + + /// + /// Writes a Double value + /// + protected abstract void Write(string field, Double value); + + /// + /// Writes a String value + /// + protected abstract void Write(string field, String value); + + /// + /// Writes a set of bytes + /// + protected abstract void Write(string field, ByteString value); + + /// + /// Writes a message or group as a field + /// + protected abstract void WriteMessageOrGroup(string field, IMessageLite message); + + /// + /// Writes a System.Enum by the numeric and textual value + /// + protected abstract void WriteEnum(string field, int number, string name); + + /// + /// Writes a field of the type determined by field.FieldType + /// + 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(); + } + } + + /// + /// Writes an array of field values + /// + protected virtual void WriteArray(FieldType fieldType, string field, IEnumerable items) + { + foreach (object obj in items) + WriteField(fieldType, field, obj); + } + + /// + /// Writes a numeric unknown field of wire type: Fixed32, Fixed64, or Variant + /// + [CLSCompliant(false)] + protected virtual void WriteUnknown(WireFormat.WireType wireType, int fieldNumber, ulong value) + { } + + /// + /// Writes an unknown field, Expect WireType of GroupStart or LengthPrefix + /// + [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(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Group, fieldName, list); } + + void ICodedOutputStream.WriteMessageArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Message, fieldName, list); } + + void ICodedOutputStream.WriteStringArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.String, fieldName, list); } + + void ICodedOutputStream.WriteBytesArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Bytes, fieldName, list); } + + void ICodedOutputStream.WriteBoolArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Bool, fieldName, list); } + + void ICodedOutputStream.WriteInt32Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Int32, fieldName, list); } + + void ICodedOutputStream.WriteSInt32Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.SInt32, fieldName, list); } + + void ICodedOutputStream.WriteUInt32Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.UInt32, fieldName, list); } + + void ICodedOutputStream.WriteFixed32Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Fixed32, fieldName, list); } + + void ICodedOutputStream.WriteSFixed32Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.SFixed32, fieldName, list); } + + void ICodedOutputStream.WriteInt64Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Int64, fieldName, list); } + + void ICodedOutputStream.WriteSInt64Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.SInt64, fieldName, list); } + + void ICodedOutputStream.WriteUInt64Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.UInt64, fieldName, list); } + + void ICodedOutputStream.WriteFixed64Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Fixed64, fieldName, list); } + + void ICodedOutputStream.WriteSFixed64Array(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.SFixed64, fieldName, list); } + + void ICodedOutputStream.WriteDoubleArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Double, fieldName, list); } + + void ICodedOutputStream.WriteFloatArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Float, fieldName, list); } + + void ICodedOutputStream.WriteEnumArray(int fieldNumber, string fieldName, System.Collections.Generic.IEnumerable 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 list) + { WriteArray(FieldType.Bool, fieldName, list); } + + void ICodedOutputStream.WritePackedInt32Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Int32, fieldName, list); } + + void ICodedOutputStream.WritePackedSInt32Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.SInt32, fieldName, list); } + + void ICodedOutputStream.WritePackedUInt32Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.UInt32, fieldName, list); } + + void ICodedOutputStream.WritePackedFixed32Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Fixed32, fieldName, list); } + + void ICodedOutputStream.WritePackedSFixed32Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.SFixed32, fieldName, list); } + + void ICodedOutputStream.WritePackedInt64Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Int64, fieldName, list); } + + void ICodedOutputStream.WritePackedSInt64Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.SInt64, fieldName, list); } + + void ICodedOutputStream.WritePackedUInt64Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.UInt64, fieldName, list); } + + void ICodedOutputStream.WritePackedFixed64Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Fixed64, fieldName, list); } + + void ICodedOutputStream.WritePackedSFixed64Array(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.SFixed64, fieldName, list); } + + void ICodedOutputStream.WritePackedDoubleArray(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Double, fieldName, list); } + + void ICodedOutputStream.WritePackedFloatArray(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable list) + { WriteArray(FieldType.Float, fieldName, list); } + + void ICodedOutputStream.WritePackedEnumArray(int fieldNumber, string fieldName, int computedSize, System.Collections.Generic.IEnumerable 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 +{ + /// + /// 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 . + /// + 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 }; } + } + + /// + /// Constructs the XmlFormatReader using the stream provided as the xml + /// + public XmlFormatReader(Stream input) : this(XmlReader.Create(input, DefaultSettings)) { } + /// + /// Constructs the XmlFormatReader using the string provided as the xml to be read + /// + public XmlFormatReader(String input) : this(XmlReader.Create(new StringReader(input))) { } + /// + /// Constructs the XmlFormatReader using the xml in the TextReader + /// + public XmlFormatReader(TextReader input) : this(XmlReader.Create(input)) { } + /// + /// Constructs the XmlFormatReader with the XmlReader + /// + public XmlFormatReader(XmlReader input) : this(input, XmlReaderOptions.None) { } + /// + /// Constructs the XmlFormatReader with the XmlReader and options + /// + public XmlFormatReader(XmlReader input, XmlReaderOptions options) + { + _input = input; + _rootElementName = DefaultRootElementName; + Options = options; + } + + /// + /// Gets or sets the options to use when reading the xml + /// + public XmlReaderOptions Options { get; set; } + + /// + /// Gets or sets the default element name to use when using the Merge<TBuilder>() + /// + 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(); + } + + /// + /// Merge the provided builder as an element named in the current context + /// + public override TBuilder Merge(TBuilder builder, ExtensionRegistry registry) + { return Merge(_rootElementName, builder, registry); } + + /// + /// Merge the provided builder as an element of the current context + /// + public TBuilder Merge(string element, TBuilder builder) where TBuilder : IBuilderLite + { return Merge(element, builder, ExtensionRegistry.Empty); } + + /// + /// Merge the provided builder as an element of the current context + /// + public TBuilder Merge(string element, TBuilder builder, ExtensionRegistry registry) where TBuilder : IBuilderLite + { + string field; + Assert(PeekNext(out field) && field == element); + ReadMessage(builder, registry); + return builder; + } + + /// + /// Peeks at the next field in the input stream and returns what information is available. + /// + /// + /// 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. + /// + protected override bool PeekNext(out string field) + { + NextElement(); + if(_input.IsStartElement()) + { + field = _input.LocalName; + return true; + } + field = null; + return false; + } + + /// + /// Causes the reader to skip past this field + /// + 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(); + } + } + + /// + /// 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. + /// + 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); + } + + /// + /// Returns true if it was able to read a String from the input + /// + protected override bool ReadAsText(ref string value, Type type) + { + Assert(_input.NodeType == XmlNodeType.Element); + value = _input.ReadElementContentAsString(); + + return true; + } + + /// + /// Merges the input stream into the provided IBuilderLite + /// + 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 NonNestedArrayItems(string field) + { + return base.ForeachArrayItem(field); + } + + /// + /// Cursors through the array elements and stops at the end of the array + /// + protected override IEnumerable 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 +{ + /// + /// 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 . + /// + 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}; } + } + + /// + /// Constructs the XmlFormatWriter to write to the given TextWriter + /// + public XmlFormatWriter(TextWriter output) : this(XmlWriter.Create(output, DefaultSettings)) { } + /// + /// Constructs the XmlFormatWriter to write to the given stream + /// + public XmlFormatWriter(Stream output) : this(XmlWriter.Create(output, DefaultSettings)) { } + /// + /// Constructs the XmlFormatWriter to write to the given XmlWriter + /// + public XmlFormatWriter(XmlWriter output) + { + _output = output; + _rootElementName = DefaultRootElementName; + } + + /// + /// Closes the underlying XmlTextWriter + /// + protected override void Dispose(bool disposing) + { + if(disposing) + _output.Close(); + } + + /// + /// Gets or sets the default element name to use when using the Merge<TBuilder>() + /// + public string RootElementName + { + get { return _rootElementName; } + set { ThrowHelper.ThrowIfNull(value, "RootElementName"); _rootElementName = value; } + } + + /// + /// Gets or sets the options to use while generating the XML + /// + public XmlWriterOptions Options { get; set; } + + private bool TestOption(XmlWriterOptions option) { return (Options & option) != 0; } + + /// + /// Writes a message as an element using the name defined in + /// + public override void WriteMessage(IMessageLite message) + { WriteMessage(_rootElementName, message); } + + /// + /// Writes a message as an element with the given name + /// + 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(); + } + + /// + /// Writes a message + /// + protected override void WriteMessageOrGroup(string field, IMessageLite message) + { + _output.WriteStartElement(field); + + if (TestOption(XmlWriterOptions.OutputJsonTypes)) + _output.WriteAttributeString("type", "object"); + + message.WriteTo(this); + _output.WriteEndElement(); + } + + /// + /// Writes a String value + /// + 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 '', rather as '' + if (_output.WriteState == WriteState.Element) + _output.WriteRaw(""); + + _output.WriteEndElement(); + } + + /// + /// Writes an array of field values + /// + 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); + } + + /// + /// Writes a System.Enum by the numeric and textual value + /// + 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 +{ + /// + /// Options available for the xml reader output + /// + [Flags] + public enum XmlReaderOptions + { + /// Simple xml formatting with no attributes + None, + /// Requires that arrays items are nested in an <item> element + 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 +{ + /// + /// Options available for the xml writer output + /// + [Flags] + public enum XmlWriterOptions + { + /// Simple xml formatting with no attributes + None, + /// Writes the 'value' attribute on all enumerations with the numeric identifier + OutputEnumValues = 0x1, + /// Embeds array items into child <item> elements + OutputNestedArrays = 0x4, + /// Outputs the 'type' attribute for compatibility with the JsonReaderWriterFactory + /// This option must, by nessessity, also enable NestedArrayItems + 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)) { -- cgit v1.2.3