Use the original name in JSON formatting.

(JSON parsing already does the right thing.)
pull/1401/head
Jon Skeet 9 years ago
parent 84ea2c7a81
commit 790f4c8e37
  1. 2
      csharp/src/Google.Protobuf/Google.Protobuf.nuspec
  2. 45
      csharp/src/Google.Protobuf/JsonFormatter.cs

@ -33,10 +33,12 @@
<dependency id="System.Linq.Expressions" version="4.0.0" /> <dependency id="System.Linq.Expressions" version="4.0.0" />
<dependency id="System.ObjectModel" version="4.0.0" /> <dependency id="System.ObjectModel" version="4.0.0" />
<dependency id="System.Reflection" version="4.0.0" /> <dependency id="System.Reflection" version="4.0.0" />
<dependency id="System.Reflection.Extensions" version="4.0.0" />
<dependency id="System.Runtime" version="4.0.0" /> <dependency id="System.Runtime" version="4.0.0" />
<dependency id="System.Runtime.Extensions" version="4.0.0" /> <dependency id="System.Runtime.Extensions" version="4.0.0" />
<dependency id="System.Text.Encoding" version="4.0.0" /> <dependency id="System.Text.Encoding" version="4.0.0" />
<dependency id="System.Text.RegularExpressions" version="4.0.0" /> <dependency id="System.Text.RegularExpressions" version="4.0.0" />
<dependency id="System.Threading" version="4.0.0" />
</group> </group>
</dependencies> </dependencies>
</metadata> </metadata>

@ -39,6 +39,7 @@ using Google.Protobuf.WellKnownTypes;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
namespace Google.Protobuf namespace Google.Protobuf
{ {
@ -420,9 +421,10 @@ namespace Google.Protobuf
} }
else if (value is System.Enum) else if (value is System.Enum)
{ {
if (System.Enum.IsDefined(value.GetType(), value)) string name = OriginalEnumValueHelper.GetOriginalName(value);
if (name != null)
{ {
WriteString(writer, value.ToString()); WriteString(writer, name);
} }
else else
{ {
@ -877,5 +879,44 @@ namespace Google.Protobuf
TypeRegistry = ProtoPreconditions.CheckNotNull(typeRegistry, nameof(typeRegistry)); TypeRegistry = ProtoPreconditions.CheckNotNull(typeRegistry, nameof(typeRegistry));
} }
} }
// Effectively a cache of mapping from enum values to the original name as specified in the proto file,
// fetched by reflection.
// The need for this is unfortunate, as is its unbounded size, but realistically it shouldn't cause issues.
private static class OriginalEnumValueHelper
{
// TODO: In the future we might want to use ConcurrentDictionary, at the point where all
// the platforms we target have it.
private static readonly Dictionary<System.Type, Dictionary<object, string>> dictionaries
= new Dictionary<System.Type, Dictionary<object, string>>();
internal static string GetOriginalName(object value)
{
var enumType = value.GetType();
Dictionary<object, string> nameMapping;
lock (dictionaries)
{
if (!dictionaries.TryGetValue(enumType, out nameMapping))
{
nameMapping = GetNameMapping(enumType);
dictionaries[enumType] = nameMapping;
}
}
string originalName;
// If this returns false, originalName will be null, which is what we want.
nameMapping.TryGetValue(value, out originalName);
return originalName;
}
private static Dictionary<object, string> GetNameMapping(System.Type enumType) =>
enumType.GetTypeInfo().DeclaredFields
.Where(f => f.IsStatic)
.ToDictionary(f => f.GetValue(null),
f => f.GetCustomAttributes<OriginalNameAttribute>()
.FirstOrDefault()
// If the attribute hasn't been applied, fall back to the name of the field.
?.Name ?? f.Name);
}
} }
} }

Loading…
Cancel
Save