36

I have the following code

[DataContract] public enum StatusType { [EnumMember(Value = "A")] All, [EnumMember(Value = "I")] InProcess, [EnumMember(Value = "C")] Complete, } 

I'd like to do the following:

 var s = "C"; StatusType status = SerializerHelper.ToEnum<StatusType>(s); //status is now StatusType.Complete string newString = SerializerHelper.ToEnumString<StatusType>(status); //newString is now "C" 

I've done the second part using DataContractSerializer (see code below), but it seems like a lot of work.

Am I missing something obvious? Ideas? Thanks.

 public static string ToEnumString<T>(T type) { string s; using (var ms = new MemoryStream()) { var ser = new DataContractSerializer(typeof(T)); ser.WriteObject(ms, type); ms.Position = 0; var sr = new StreamReader(ms); s = sr.ReadToEnd(); } using (var xml = new XmlTextReader(s, XmlNodeType.Element, null)) { xml.MoveToContent(); xml.Read(); return xml.Value; } } 
1

6 Answers 6

54

Here is my proposition - it should give you the idea on how to do this (check also Getting attributes of Enum's value):

public static string ToEnumString<T>(T type) { var enumType = typeof (T); var name = Enum.GetName(enumType, type); var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single(); return enumMemberAttribute.Value; } public static T ToEnum<T>(string str) { var enumType = typeof(T); foreach (var name in Enum.GetNames(enumType)) { var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single(); if (enumMemberAttribute.Value == str) return (T)Enum.Parse(enumType, name); } //throw exception or whatever handling you want or return default(T); } 
Sign up to request clarification or add additional context in comments.

Comments

20

If your project references Newtonsoft.Json (what doesn't these days?!), then there is a simple one line solution that doesn't need reflection:

public static string ToEnumString<T>(T value) { return JsonConvert.SerializeObject(value).Replace("\"", ""); } public static T ToEnum<T>(string value) { return JsonConvert.DeserializeObject<T>($"\"{value}\""); } 

The ToEnumString method will only work if you have the StringEnumConverter registered in your JsonSerializerSettings (see JavaScriptSerializer - JSON serialization of enum as string), e.g.

JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Converters = { new StringEnumConverter() } }; 

Another advantage of this method is that if only some of your enum elements have the member attribute, things still work as expected, e.g.

public enum CarEnum { Ford, Volkswagen, [EnumMember(Value = "Aston Martin")] AstonMartin } 

2 Comments

There is an overload for string JsonConvert.SerializeObject(object? value, params JsonConvert[] converters). So ToEnumString can be changed to: return JsonConvert.SerializeObject(value, new StringEnumConverter()).Replace("\"", ""); This negates the need to register the converter separately.
A great addition to the proposed solution and avoiding the need to register another convertor - works a treat!
10

Using extensions and C# 7.3 constraints

 public static class EnumMemberExtensions { public static string ToEnumString<T>(this T type) where T : Enum { var enumType = typeof(T); var name = Enum.GetName(enumType, type); var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single(); return enumMemberAttribute.Value; } public static T ToEnum<T>(this string str) where T : Enum { var enumType = typeof(T); foreach (var name in Enum.GetNames(enumType)) { var enumMemberAttribute = ((EnumMemberAttribute[])enumType.GetField(name).GetCustomAttributes(typeof(EnumMemberAttribute), true)).Single(); if (enumMemberAttribute.Value == str) return (T)Enum.Parse(enumType, name); } //throw exception or whatever handling you want or return default; } } 

1 Comment

Is there a .net core solution equivalent to this?
4

You can use reflection to get the value of the EnumMemberAttribute.

public static string ToEnumString<T>(T instance) { if (!typeof(T).IsEnum) throw new ArgumentException("instance", "Must be enum type"); string enumString = instance.ToString(); var field = typeof(T).GetField(enumString); if (field != null) // instance can be a number that was cast to T, instead of a named value, or could be a combination of flags instead of a single value { var attr = (EnumMemberAttribute)field.GetCustomAttributes(typeof(EnumMemberAttribute), false).SingleOrDefault(); if (attr != null) // if there's no EnumMember attr, use the default value enumString = attr.Value; } return enumString; } 

Depending on how your ToEnum works, you might want to use this sort of approach there as well. Also, the type can be inferred when calling ToEnumString, e.g. SerializerHelper.ToEnumString(status);

Comments

2

This example shows how to convert enums using the DescriptionAttribute, the EnumMemberAttribute and the property name:

using System; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.Serialization; 
 public static class EnumExtensions { public static T ToEnumByAttributes<T>(this string value) where T:Enum { var enumType = typeof(T); foreach (var name in Enum.GetNames(enumType)) { var field = enumType.GetField(name); if(field == null) continue; var enumMemberAttribute = GetEnumMemberAttribute(field); if (enumMemberAttribute != null && enumMemberAttribute.Value == value) { return (T)Enum.Parse(enumType, name); } var descriptionAttribute = GetDescriptionAttribute(field); if (descriptionAttribute != null && descriptionAttribute.Description == value) { return (T)Enum.Parse(enumType, name); } if (name == value) { return (T)Enum.Parse(enumType, name); } } throw new ArgumentOutOfRangeException(nameof(value), value, $"The value could not be mapped to type {enumType.FullName}"); } public static string ToStringByAttributes(this Enum value) { var field = value .GetType() .GetField(value.ToString()); if (field == null) return string.Empty; var enumMemberAttribute = GetEnumMemberAttribute(field); if (enumMemberAttribute != null) { return enumMemberAttribute.Value ?? string.Empty; } var descriptionAttribute = GetDescriptionAttribute(field); if (descriptionAttribute != null) { return descriptionAttribute.Description; } return value.ToString(); } private static DescriptionAttribute? GetDescriptionAttribute(FieldInfo field) { return field .GetCustomAttributes(typeof(DescriptionAttribute), false) .OfType<DescriptionAttribute>() .SingleOrDefault(); } private static EnumMemberAttribute? GetEnumMemberAttribute(FieldInfo field) { return field .GetCustomAttributes(typeof(EnumMemberAttribute), false) .OfType<EnumMemberAttribute>() .SingleOrDefault(); } } 

NUnit Tests:

 [TestFixture] public sealed class EnumExtensionsTests { public enum TestEnum { [EnumMember(Value = "A")] Alpha, [Description("O")] Omega } [Test] public void ShouldSerialize_FromEnumAttribute() { var result = TestEnum.Alpha.ToStringByAttributes(); Assert.That(result, Is.EqualTo("A")); } [Test] public void ShouldSerialize_FromDescriptionAttribute() { var result = TestEnum.Omega.ToStringByAttributes(); Assert.That(result, Is.EqualTo("O")); } [Test] public void ShouldDeserialize_FromEnumAttribute() { var result = "A".ToEnumByAttributes<TestEnum>(); Assert.That(result, Is.EqualTo(TestEnum.Alpha)); } [Test] public void ShouldDeserialize_FromDescriptionAttribute() { var result = "O".ToEnumByAttributes<TestEnum>(); Assert.That(result, Is.EqualTo(TestEnum.Omega)); } [Test] public void ShouldDeserialize_FromPropertyName() { var result = "Alpha".ToEnumByAttributes<TestEnum>(); Assert.That(result, Is.EqualTo(TestEnum.Alpha)); } } 

Comments

0

Personally, I use the Reefact.JsonEnumValueBinding library because I mostly use Json instead of Xml nowdays. Would this be suitable? ?

public enum StatusType { [JsonEnumValue("A")] All, [JsonEnumValue("I")] InProcess, [JsonEnumValue("C")] Complete, } 

So you can do the following:

JsonSerializerOptions options = new() { Converters = { new JsonEnumValueConverterFactory() } }; string s = "C"; // s is "C" StatusType deserializedStatus = JsonSerializer.Deserialize<StatusType>($"\"{s}\"", options); // status is now StatusType.Complete string serializedStatus = JsonSerializer.Serialize(deserializedStatus, options); // serializedStatus is now "\"C\"" string newString = serializedStatus[1..^1]; // newString is now "C" 

Unfortunately, Json serialization introduces quotation marks, but perhaps this doesn't pose any real problems for you in your real-life use case?

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.