Browse Source

Added initial DictionaryReader/Writer implementations

csharptest 14 years ago
parent
commit
4dc0dfb154

+ 6 - 0
src/ProtoBench/Program.cs

@@ -137,6 +137,10 @@ namespace Google.ProtocolBuffers.ProtoBench
                 new JsonFormatWriter(temp).WriteMessage(sampleMessage);
                 string jsonMessageText = temp.ToString();
 
+                IDictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
+                new DictionaryWriter(dictionary).WriteMessage(sampleMessage);
+
+
                 //Serializers
                 if(!FastTest) RunBenchmark("Serialize to byte string", inputData.Length, () => sampleMessage.ToByteString());
                 RunBenchmark("Serialize to byte array", inputData.Length, () => sampleMessage.ToByteArray());
@@ -145,6 +149,7 @@ namespace Google.ProtocolBuffers.ProtoBench
 
                 RunBenchmark("Serialize to xml", xmlMessageText.Length, () => new XmlFormatWriter(new StringWriter()).WriteMessage(sampleMessage));
                 RunBenchmark("Serialize to json", jsonMessageText.Length, () => new JsonFormatWriter(new StringWriter()).WriteMessage(sampleMessage));
+                RunBenchmark("Serialize to dictionary", sampleMessage.SerializedSize, () => new DictionaryWriter().WriteMessage(sampleMessage));
 
                 //Deserializers
                 if (!FastTest) RunBenchmark("Deserialize from byte string", inputData.Length,
@@ -168,6 +173,7 @@ namespace Google.ProtocolBuffers.ProtoBench
 
                 RunBenchmark("Deserialize from xml", xmlMessageText.Length, () => new XmlFormatReader(xmlMessageText).Merge(defaultMessage.WeakCreateBuilderForType()).WeakBuild());
                 RunBenchmark("Deserialize from json", jsonMessageText.Length, () => new JsonFormatReader(jsonMessageText).Merge(defaultMessage.WeakCreateBuilderForType()).WeakBuild());
+                RunBenchmark("Deserialize from dictionary", sampleMessage.SerializedSize, () => new DictionaryReader(dictionary).Merge(defaultMessage.WeakCreateBuilderForType()).WeakBuild());
 
                 Console.WriteLine();
                 return true;

+ 9 - 4
src/ProtocolBuffers.Test/CompatTests/CompatibilityTests.cs

@@ -14,6 +14,11 @@ namespace Google.ProtocolBuffers.CompatTests
             where TMessage : IMessageLite<TMessage, TBuilder>
             where TBuilder : IBuilderLite<TMessage, TBuilder>;
 
+        protected virtual void AssertOutputEquals(object lhs, object rhs)
+        {
+            Assert.AreEqual(lhs, rhs);
+        }
+
         [Test]
         public virtual void RoundTripMessage1OptimizeSize()
         {
@@ -23,7 +28,7 @@ namespace Google.ProtocolBuffers.CompatTests
             SizeMessage1 copy = DeerializeMessage<SizeMessage1, SizeMessage1.Builder>(content, SizeMessage1.CreateBuilder(), ExtensionRegistry.Empty).Build();
 
             Assert.AreEqual(msg, copy);
-            Assert.AreEqual(content, SerializeMessage<SizeMessage1,SizeMessage1.Builder>(copy));
+            AssertOutputEquals(content, SerializeMessage<SizeMessage1, SizeMessage1.Builder>(copy));
             Assert.AreEqual(TestResources.google_message1, copy.ToByteArray());
         }
 
@@ -36,7 +41,7 @@ namespace Google.ProtocolBuffers.CompatTests
             SizeMessage2 copy = DeerializeMessage<SizeMessage2, SizeMessage2.Builder>(content, SizeMessage2.CreateBuilder(), ExtensionRegistry.Empty).Build();
 
             Assert.AreEqual(msg, copy);
-            Assert.AreEqual(content, SerializeMessage<SizeMessage2, SizeMessage2.Builder>(copy));
+            AssertOutputEquals(content, SerializeMessage<SizeMessage2, SizeMessage2.Builder>(copy));
             Assert.AreEqual(TestResources.google_message2, copy.ToByteArray());
         }
 
@@ -49,7 +54,7 @@ namespace Google.ProtocolBuffers.CompatTests
             SpeedMessage1 copy = DeerializeMessage<SpeedMessage1, SpeedMessage1.Builder>(content, SpeedMessage1.CreateBuilder(), ExtensionRegistry.Empty).Build();
 
             Assert.AreEqual(msg, copy);
-            Assert.AreEqual(content, SerializeMessage<SpeedMessage1, SpeedMessage1.Builder>(copy));
+            AssertOutputEquals(content, SerializeMessage<SpeedMessage1, SpeedMessage1.Builder>(copy));
             Assert.AreEqual(TestResources.google_message1, copy.ToByteArray());
         }
 
@@ -62,7 +67,7 @@ namespace Google.ProtocolBuffers.CompatTests
             SpeedMessage2 copy = DeerializeMessage<SpeedMessage2, SpeedMessage2.Builder>(content, SpeedMessage2.CreateBuilder(), ExtensionRegistry.Empty).Build();
 
             Assert.AreEqual(msg, copy);
-            Assert.AreEqual(content, SerializeMessage<SpeedMessage2, SpeedMessage2.Builder>(copy));
+            AssertOutputEquals(content, SerializeMessage<SpeedMessage2, SpeedMessage2.Builder>(copy));
             Assert.AreEqual(TestResources.google_message2, copy.ToByteArray());
         }
 

+ 37 - 0
src/ProtocolBuffers.Test/CompatTests/DictionaryCompatibilityTests.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Google.ProtocolBuffers.Serialization;
+using NUnit.Framework;
+
+namespace Google.ProtocolBuffers.CompatTests
+{
+    [TestFixture]
+    public class DictionaryCompatibilityTests : CompatibilityTests
+    {
+        protected override object SerializeMessage<TMessage, TBuilder>(TMessage message)
+        {
+            DictionaryWriter writer = new DictionaryWriter();
+            writer.WriteMessage(message);
+            return writer.ToDictionary();
+        }
+
+        protected override TBuilder DeerializeMessage<TMessage, TBuilder>(object message, TBuilder builder, ExtensionRegistry registry)
+        {
+            new DictionaryReader((IDictionary<string, object>)message).Merge(builder);
+            return builder;
+        }
+
+        protected override void AssertOutputEquals(object lhs, object rhs)
+        {
+            IDictionary<string, object> left = (IDictionary<string, object>)lhs;
+            IDictionary<string, object> right = (IDictionary<string, object>)rhs;
+
+            Assert.AreEqual(
+                String.Join(",", new List<string>(left.Keys).ToArray()),
+                String.Join(",", new List<string>(right.Keys).ToArray())
+            );
+        }
+    }
+}

+ 1 - 0
src/ProtocolBuffers.Test/ProtocolBuffers.Test.csproj

@@ -77,6 +77,7 @@
     <Compile Include="Collections\PopsicleListTest.cs" />
     <Compile Include="CompatTests\BinaryCompatibilityTests.cs" />
     <Compile Include="CompatTests\CompatibilityTests.cs" />
+    <Compile Include="CompatTests\DictionaryCompatibilityTests.cs" />
     <Compile Include="CompatTests\JsonCompatibilityTests.cs" />
     <Compile Include="CompatTests\TestResources.Designer.cs">
       <AutoGen>True</AutoGen>

+ 2 - 0
src/ProtocolBuffers/ProtocolBuffers.csproj

@@ -184,6 +184,8 @@
     <Compile Include="Serialization\AbstractTextReader.cs" />
     <Compile Include="Serialization\AbstractTextWriter.cs" />
     <Compile Include="Serialization\AbstractWriter.cs" />
+    <Compile Include="Serialization\DictionaryReader.cs" />
+    <Compile Include="Serialization\DictionaryWriter.cs" />
     <Compile Include="Serialization\JsonFormatReader.cs" />
     <Compile Include="Serialization\JsonFormatWriter.cs" />
     <Compile Include="Serialization\JsonTextCursor.cs" />

+ 0 - 7
src/ProtocolBuffers/Serialization/AbstractWriter.cs

@@ -41,13 +41,6 @@ namespace Google.ProtocolBuffers.Serialization
         /// </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>

+ 227 - 0
src/ProtocolBuffers/Serialization/DictionaryReader.cs

@@ -0,0 +1,227 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+    /// <summary>
+    /// Allows reading messages from a name/value dictionary
+    /// </summary>
+    public class DictionaryReader : AbstractReader
+    {
+        private readonly IEnumerator<KeyValuePair<string, object>> _input;
+        private bool _ready;
+
+        /// <summary>
+        /// Creates a dictionary reader from an enumeration of KeyValuePair data, like an IDictionary
+        /// </summary>
+        public DictionaryReader(IEnumerable<KeyValuePair<string, object>> input)
+        {
+            _input = input.GetEnumerator();
+            _ready = _input.MoveNext();
+        }
+
+        /// <summary>
+        /// Merges the contents of stream into the provided message builder
+        /// </summary>
+        public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
+        {
+            builder.WeakMergeFrom(this, registry);
+            return builder;
+        }
+
+        /// <summary>
+        /// Peeks at the next field in the input stream and returns what information is available.
+        /// </summary>
+        /// <remarks>
+        /// This may be called multiple times without actually reading the field.  Only after the field
+        /// is either read, or skipped, should PeekNext return a different value.
+        /// </remarks>
+        protected override bool PeekNext(out string field)
+        {
+            field = _ready ? _input.Current.Key : null;
+            return _ready;
+        }
+
+        /// <summary>
+        /// Causes the reader to skip past this field
+        /// </summary>
+        protected override void Skip()
+        {
+            _ready = _input.MoveNext();
+        }
+
+        private bool GetValue<T>(ref T value)
+        {
+            if (!_ready) return false;
+
+            object obj = _input.Current.Value;
+            if (obj is T)
+                value = (T)obj;
+            else
+            {
+                try 
+                {
+                    if (obj is IConvertible)
+                        value = (T)Convert.ChangeType(obj, typeof(T));
+                    else
+                        value = (T)obj;
+                }
+                catch
+                {
+                    _ready = _input.MoveNext(); 
+                    return false;
+                }
+            }
+            _ready = _input.MoveNext();
+            return true;
+        }
+
+        /// <summary>
+        /// Returns true if it was able to read a Boolean from the input
+        /// </summary>
+        protected override bool Read(ref bool value)
+        {
+            return GetValue(ref value); 
+        }
+
+        /// <summary>
+        /// Returns true if it was able to read a Int32 from the input
+        /// </summary>
+        protected override bool Read(ref int value)
+        {
+            return GetValue(ref value);
+        }
+
+        /// <summary>
+        /// Returns true if it was able to read a UInt32 from the input
+        /// </summary>
+        [CLSCompliant(false)]
+        protected override bool Read(ref uint value)
+        {
+            return GetValue(ref value);
+        }
+
+        /// <summary>
+        /// Returns true if it was able to read a Int64 from the input
+        /// </summary>
+        protected override bool Read(ref long value)
+        {
+            return GetValue(ref value);
+        }
+
+        /// <summary>
+        /// Returns true if it was able to read a UInt64 from the input
+        /// </summary>
+        [CLSCompliant(false)]
+        protected override bool Read(ref ulong value)
+        {
+            return GetValue(ref value);
+        }
+
+        /// <summary>
+        /// Returns true if it was able to read a Single from the input
+        /// </summary>
+        protected override bool Read(ref float value)
+        {
+            return GetValue(ref value);
+        }
+
+        /// <summary>
+        /// Returns true if it was able to read a Double from the input
+        /// </summary>
+        protected override bool Read(ref double value)
+        {
+            return GetValue(ref value);
+        }
+
+        /// <summary>
+        /// Returns true if it was able to read a String from the input
+        /// </summary>
+        protected override bool Read(ref string value)
+        {
+            return GetValue(ref value);
+        }
+
+        /// <summary>
+        /// Returns true if it was able to read a ByteString from the input
+        /// </summary>
+        protected override bool Read(ref ByteString value)
+        {
+            byte[] rawbytes = null;
+            if (GetValue(ref rawbytes))
+            {
+                value = ByteString.AttachBytes(rawbytes);
+                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)
+        {
+            return GetValue(ref value);
+        }
+
+        /// <summary>
+        /// Merges the input stream into the provided IBuilderLite 
+        /// </summary>
+        protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
+        {
+            IDictionary<string, object> values = null;
+            if (GetValue(ref values))
+            {
+                new DictionaryReader(values).Merge(builder, registry);
+                return true;
+            }
+            return false;
+        }
+
+        public override bool ReadArray<T>(FieldType type, string field, ICollection<T> items)
+        {
+            object[] array = null;
+            if (GetValue(ref array))
+            {
+                foreach (T item in array)
+                    items.Add(item);
+                return true;
+            }
+            return false;
+        }
+        
+        public override bool ReadEnumArray(string field, ICollection<object> items)
+        {
+            object[] array = null;
+            if (GetValue(ref array))
+            {
+                foreach (object item in array)
+                    items.Add(item);
+                return true;
+            }
+            return false;
+        }
+
+        public override bool ReadMessageArray<T>(string field, ICollection<T> items, IMessageLite messageType, ExtensionRegistry registry)
+        {
+            object[] array = null;
+            if (GetValue(ref array))
+            {
+                foreach (IDictionary<string, object> item in array)
+                {
+                    IBuilderLite builder = messageType.WeakCreateBuilderForType();
+                    new DictionaryReader(item).Merge(builder);
+                    items.Add((T)builder.WeakBuild());
+                }
+                return true;
+            }
+            return false;
+        }
+
+        public override bool ReadGroupArray<T>(string field, ICollection<T> items, IMessageLite messageType, ExtensionRegistry registry)
+        { return ReadMessageArray(field, items, messageType, registry); }
+    }
+}

+ 172 - 0
src/ProtocolBuffers/Serialization/DictionaryWriter.cs

@@ -0,0 +1,172 @@
+using System;
+using System.Collections.Generic;
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+    /// <summary>
+    /// Allows writing messages to a name/value dictionary
+    /// </summary>
+    public class DictionaryWriter : AbstractWriter
+    {
+        private readonly IDictionary<string, object> _output;
+
+        /// <summary>
+        /// Constructs a writer using a new dictionary
+        /// </summary>
+        public DictionaryWriter()
+            : this(new Dictionary<string,object>())
+        { }
+
+        /// <summary>
+        /// Constructs a writer using an existing dictionary
+        /// </summary>
+        public DictionaryWriter(IDictionary<string, object> output)
+        {
+            ThrowHelper.ThrowIfNull(output, "output");
+            _output = output;
+        }
+
+        /// <summary>
+        /// Accesses the dictionary that is backing this writer
+        /// </summary>
+        public IDictionary<string, object> ToDictionary() { return _output; }
+
+            /// <summary>
+        /// Writes the message to the the formatted stream.
+        /// </summary>
+        public override void WriteMessage(IMessageLite message)
+        {
+            message.WriteTo(this);
+        }
+
+        /// <summary>
+        /// Writes a Boolean value
+        /// </summary>
+        protected override void Write(string field, bool value)
+        {
+            _output[field] = value;
+        }
+
+        /// <summary>
+        /// Writes a Int32 value
+        /// </summary>
+        protected override void Write(string field, int value)
+        {
+            _output[field] = value;
+        }
+
+        /// <summary>
+        /// Writes a UInt32 value
+        /// </summary>
+        [CLSCompliant(false)]
+        protected override void Write(string field, uint value)
+        {
+            _output[field] = value;
+        }
+
+        /// <summary>
+        /// Writes a Int64 value
+        /// </summary>
+        protected override void Write(string field, long value)
+        {
+            _output[field] = value;
+        }
+
+        /// <summary>
+        /// Writes a UInt64 value
+        /// </summary>
+        [CLSCompliant(false)]
+        protected override void Write(string field, ulong value)
+        {
+            _output[field] = value;
+        }
+
+        /// <summary>
+        /// Writes a Single value
+        /// </summary>
+        protected override void Write(string field, float value)
+        {
+            _output[field] = value;
+        }
+
+        /// <summary>
+        /// Writes a Double value
+        /// </summary>
+        protected override void Write(string field, double value)
+        {
+            _output[field] = value;
+        }
+
+        /// <summary>
+        /// Writes a String value
+        /// </summary>
+        protected override void Write(string field, string value)
+        {
+            _output[field] = value;
+        }
+
+        /// <summary>
+        /// Writes a set of bytes
+        /// </summary>
+        protected override void Write(string field, ByteString value)
+        {
+            _output[field] = value.ToByteArray();
+        }
+
+        /// <summary>
+        /// Writes a message or group as a field
+        /// </summary>
+        protected override void WriteMessageOrGroup(string field, IMessageLite message)
+        {
+            DictionaryWriter writer = new DictionaryWriter();
+            writer.WriteMessage(message);
+
+            _output[field] = writer.ToDictionary();
+        }
+
+        /// <summary>
+        /// Writes a System.Enum by the numeric and textual value
+        /// </summary>
+        protected override void WriteEnum(string field, int number, string name)
+        {
+            _output[field] = number;
+        }
+
+        /// <summary>
+        /// Writes an array of field values
+        /// </summary>
+        protected override void WriteArray(FieldType fieldType, string field, System.Collections.IEnumerable items)
+        {
+            List<object> objects = new List<object>();
+            foreach (object o in items)
+            {
+                switch (fieldType)
+                {
+                    case FieldType.Group:
+                    case FieldType.Message:
+                        {
+                            DictionaryWriter writer = new DictionaryWriter();
+                            writer.WriteMessage((IMessageLite)o);
+                            objects.Add(writer.ToDictionary());
+                        }
+                        break;
+                    case FieldType.Bytes:
+                        objects.Add(((ByteString)o).ToByteArray());
+                        break;
+                    case FieldType.Enum:
+                        if (o is IEnumLite)
+                            objects.Add(((IEnumLite)o).Number);
+                        else
+                            objects.Add((int)o);
+                        break;
+                    default:
+                        objects.Add(o);
+                        break;
+                }
+            }
+
+            _output[field] = objects.ToArray();
+        }
+    }
+}

+ 0 - 10
src/ProtocolBuffers/Serialization/JsonFormatWriter.cs

@@ -263,16 +263,6 @@ namespace Google.ProtocolBuffers.Serialization
             Flush();
         }
 
-        /// <summary>
-        /// Writes a message
-        /// </summary>
-        [System.ComponentModel.Browsable(false)]
-        [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
-        public override void WriteMessage(string field, IMessageLite message)
-        {
-            WriteMessage(message);
-        }
-
         /// <summary>
         /// Used in streaming arrays of objects to the writer
         /// </summary>

+ 1 - 1
src/ProtocolBuffers/Serialization/XmlFormatWriter.cs

@@ -73,7 +73,7 @@ namespace Google.ProtocolBuffers.Serialization
         /// <summary>
         /// Writes a message as an element with the given name
         /// </summary>
-        public override void WriteMessage(string elementName, IMessageLite message)
+        public void WriteMessage(string elementName, IMessageLite message)
         {
             if (TestOption(XmlWriterOptions.OutputJsonTypes))
             {