Merge pull request #2884 from anandolee/master

Add FormatEnumsAsIntegers support for Json Format. Scale JsonFormatter.Settings for multi options.
pull/2912/head
Jie Luo 8 years ago committed by GitHub
commit 7610f101e8
  1. 39
      csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
  2. 2
      csharp/src/Google.Protobuf.Test/project.json
  3. 56
      csharp/src/Google.Protobuf/JsonFormatter.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 };

@ -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,

Loading…
Cancel
Save