Browse Source

Add FormatEnumAsInt support for Json Format. And scale JsonFormatter.Settings to multiple options.

Jie Luo 8 years ago
parent
commit
689e4bf5f4

+ 28 - 11
csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs

@@ -52,7 +52,7 @@ namespace Google.Protobuf
         [Test]
         public void DefaultValues_WhenOmitted()
         {
-            var formatter = new JsonFormatter(new JsonFormatter.Settings(formatDefaultValues: false));
+            var formatter = JsonFormatter.Default;
 
             AssertJson("{ }", formatter.Format(new ForeignMessage()));
             AssertJson("{ }", formatter.Format(new TestAllTypes()));
@@ -62,7 +62,7 @@ namespace Google.Protobuf
         [Test]
         public void DefaultValues_WhenIncluded()
         {
-            var formatter = new JsonFormatter(new JsonFormatter.Settings(formatDefaultValues: true));
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
             AssertJson("{ 'c': 0 }", formatter.Format(new ForeignMessage()));
         }
 
@@ -78,6 +78,23 @@ namespace Google.Protobuf
             AssertJson(expectedText, actualText);
         }
 
+        [Test]
+        public void EnumAsInt()
+        {
+            var message = new TestAllTypes
+            {
+                SingleForeignEnum = ForeignEnum.ForeignBar,
+                RepeatedForeignEnum = { ForeignEnum.ForeignBaz, (ForeignEnum) 100, ForeignEnum.ForeignFoo }
+            };
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatEnumsAsIntegers(true));
+            var actualText = formatter.Format(message);
+            var expectedText = "{ " +
+                               "'singleForeignEnum': 5, " +
+                               "'repeatedForeignEnum': [ 6, 100, 4 ]" +
+                               " }";
+            AssertJson(expectedText, actualText);
+        }
+
         [Test]
         public void AllSingleFields()
         {
@@ -266,9 +283,9 @@ namespace Google.Protobuf
             }
 
             // We should get the same result both with and without "format default values".
-            var formatter = new JsonFormatter(new JsonFormatter.Settings(false));
+            var formatter = JsonFormatter.Default;
             AssertJson(expectedJson, formatter.Format(message));
-            formatter = new JsonFormatter(new JsonFormatter.Settings(true));
+            formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
             AssertJson(expectedJson, formatter.Format(message));
         }
 
@@ -300,7 +317,7 @@ namespace Google.Protobuf
         {
             // The actual JSON here is very large because there are lots of fields. Just test a couple of them.
             var message = new TestWellKnownTypes { Int32Field = 10 };
-            var formatter = new JsonFormatter(new JsonFormatter.Settings(true));
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
             var actualJson = formatter.Format(message);
             Assert.IsTrue(actualJson.Contains("\"int64Field\": null"));
             Assert.IsFalse(actualJson.Contains("\"int32Field\": null"));
@@ -309,7 +326,7 @@ namespace Google.Protobuf
         [Test]
         public void OutputIsInNumericFieldOrder_NoDefaults()
         {
-            var formatter = new JsonFormatter(new JsonFormatter.Settings(false));
+            var formatter = JsonFormatter.Default;
             var message = new TestJsonFieldOrdering { PlainString = "p1", PlainInt32 = 2 };
             AssertJson("{ 'plainString': 'p1', 'plainInt32': 2 }", formatter.Format(message));
             message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };
@@ -321,7 +338,7 @@ namespace Google.Protobuf
         [Test]
         public void OutputIsInNumericFieldOrder_WithDefaults()
         {
-            var formatter = new JsonFormatter(new JsonFormatter.Settings(true));
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
             var message = new TestJsonFieldOrdering();
             AssertJson("{ 'plainString': '', 'plainInt32': 0 }", formatter.Format(message));
             message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };
@@ -485,7 +502,7 @@ namespace Google.Protobuf
         [Test]
         public void AnyWellKnownType()
         {
-            var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(Timestamp.Descriptor)));
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(Timestamp.Descriptor)));
             var timestamp = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
             var any = Any.Pack(timestamp);
             AssertJson("{ '@type': 'type.googleapis.com/google.protobuf.Timestamp', 'value': '1673-06-19T12:34:56Z' }", formatter.Format(any));
@@ -494,7 +511,7 @@ namespace Google.Protobuf
         [Test]
         public void AnyMessageType()
         {
-            var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
             var message = new TestAllTypes { SingleInt32 = 10, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } };
             var any = Any.Pack(message);
             AssertJson("{ '@type': 'type.googleapis.com/protobuf_unittest.TestAllTypes', 'singleInt32': 10, 'singleNestedMessage': { 'bb': 20 } }", formatter.Format(any));
@@ -503,7 +520,7 @@ namespace Google.Protobuf
         [Test]
         public void AnyMessageType_CustomPrefix()
         {
-            var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
             var message = new TestAllTypes { SingleInt32 = 10 };
             var any = Any.Pack(message, "foo.bar/baz");
             AssertJson("{ '@type': 'foo.bar/baz/protobuf_unittest.TestAllTypes', 'singleInt32': 10 }", formatter.Format(any));
@@ -513,7 +530,7 @@ namespace Google.Protobuf
         public void AnyNested()
         {
             var registry = TypeRegistry.FromMessages(TestWellKnownTypes.Descriptor, TestAllTypes.Descriptor);
-            var formatter = new JsonFormatter(new JsonFormatter.Settings(false, registry));
+            var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithTypeRegistry(registry));
 
             // Nest an Any as the value of an Any.
             var doubleNestedMessage = new TestAllTypes { SingleInt32 = 20 };

+ 1 - 1
csharp/src/Google.Protobuf.Test/project.json

@@ -42,4 +42,4 @@
       }
     }
   }
-}
+}

+ 49 - 7
csharp/src/Google.Protobuf/JsonFormatter.cs

@@ -375,14 +375,21 @@ namespace Google.Protobuf
             }
             else if (value is System.Enum)
             {
-                string name = OriginalEnumValueHelper.GetOriginalName(value);
-                if (name != null)
+                if (settings.FormatEnumsAsIntegers)
                 {
-                    WriteString(writer, name);
+                    WriteValue(writer, (int)value);
                 }
                 else
                 {
-                    WriteValue(writer, (int)value);
+                    string name = OriginalEnumValueHelper.GetOriginalName(value);
+                    if (name != null)
+                    {
+                        WriteString(writer, name);
+                    }
+                    else
+                    {
+                        WriteValue(writer, (int)value);
+                    }
                 }
             }
             else if (value is float || value is double)
@@ -778,7 +785,11 @@ namespace Google.Protobuf
             /// </summary>
             public TypeRegistry TypeRegistry { get; }
 
-            // TODO: Work out how we're going to scale this to multiple settings. "WithXyz" methods?
+            /// <summary>
+            /// Whether to format enums as ints. Defaults to false.
+            /// </summary>
+            public bool FormatEnumsAsIntegers { get; }
+
 
             /// <summary>
             /// Creates a new <see cref="Settings"/> object with the specified formatting of default values
@@ -795,11 +806,42 @@ namespace Google.Protobuf
             /// </summary>
             /// <param name="formatDefaultValues"><c>true</c> if default values (0, empty strings etc) should be formatted; <c>false</c> otherwise.</param>
             /// <param name="typeRegistry">The <see cref="TypeRegistry"/> to use when formatting <see cref="Any"/> messages.</param>
-            public Settings(bool formatDefaultValues, TypeRegistry typeRegistry)
+            public Settings(bool formatDefaultValues, TypeRegistry typeRegistry) : this(formatDefaultValues, typeRegistry, false)
+            {
+            }
+
+            /// <summary>
+            /// Creates a new <see cref="Settings"/> object with the specified parameters.
+            /// </summary>
+            /// <param name="formatDefaultValues"><c>true</c> if default values (0, empty strings etc) should be formatted; <c>false</c> otherwise.</param>
+            /// <param name="typeRegistry">The <see cref="TypeRegistry"/> to use when formatting <see cref="Any"/> messages. TypeRegistry.Empty will be used if it is null.</param>
+            /// <param name="formatEnumsAsIntegers"><c>true</c> to format the enums as integers; <c>false</c> to format enums as enum names.</param>
+            private Settings(bool formatDefaultValues,
+                            TypeRegistry typeRegistry,
+                            bool formatEnumsAsIntegers)
             {
                 FormatDefaultValues = formatDefaultValues;
-                TypeRegistry = ProtoPreconditions.CheckNotNull(typeRegistry, nameof(typeRegistry));
+                TypeRegistry = typeRegistry ?? TypeRegistry.Empty;
+                FormatEnumsAsIntegers = formatEnumsAsIntegers;
             }
+
+            /// <summary>
+            /// Creates a new <see cref="Settings"/> object with the specified formatting of default values and the current settings.
+            /// </summary>
+            /// <param name="formatDefaultValues"><c>true</c> if default values (0, empty strings etc) should be formatted; <c>false</c> otherwise.</param>
+            public Settings WithFormatDefaultValues(bool formatDefaultValues) => new Settings(formatDefaultValues, TypeRegistry, FormatEnumsAsIntegers);
+
+            /// <summary>
+            /// Creates a new <see cref="Settings"/> object with the specified type registry and the current settings.
+            /// </summary>
+            /// <param name="typeRegistry">The <see cref="TypeRegistry"/> to use when formatting <see cref="Any"/> messages.</param>
+            public Settings WithTypeRegistry(TypeRegistry typeRegistry) => new Settings(FormatDefaultValues, typeRegistry, FormatEnumsAsIntegers);
+
+            /// <summary>
+            /// Creates a new <see cref="Settings"/> object with the specified enums formatting option and the current settings.
+            /// </summary>
+            /// <param name="formatEnumsAsIntegers"><c>true</c> to format the enums as integers; <c>false</c> to format enums as enum names.</param>
+            public Settings WithFormatEnumsAsIntegers(bool formatEnumsAsIntegers) => new Settings(FormatDefaultValues, TypeRegistry, formatEnumsAsIntegers);
         }
 
         // Effectively a cache of mapping from enum values to the original name as specified in the proto file,