|
@@ -39,6 +39,7 @@ using Google.Protobuf.WellKnownTypes;
|
|
|
using System.IO;
|
|
|
using System.Linq;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Reflection;
|
|
|
|
|
|
namespace Google.Protobuf
|
|
|
{
|
|
@@ -420,9 +421,10 @@ namespace Google.Protobuf
|
|
|
}
|
|
|
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
|
|
|
{
|
|
@@ -877,5 +879,44 @@ namespace Google.Protobuf
|
|
|
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);
|
|
|
+ }
|
|
|
}
|
|
|
}
|