I have an object that needs to be serialized to an EDI format. For this example we’ll say it’s a car. A car might not be the best example b/c options change over time, but for the real object the Enums will never change.
I have many Enums like the following with custom attributes applied.
public enum RoofStyle
{
[DisplayText("Glass Top")]
[StringValue("GTR")]
Glass,
[DisplayText("Convertible Soft Top")]
[StringValue("CST")]
ConvertibleSoft,
[DisplayText("Hard Top")]
[StringValue("HT ")]
HardTop,
[DisplayText("Targa Top")]
[StringValue("TT ")]
Targa,
}
The Attributes are accessed via Extension methods:
public static string GetStringValue(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the stringvalue attributes
StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
typeof(StringValueAttribute), false) as StringValueAttribute[];
// Return the first if there was a match.
return attribs.Length > 0 ? attribs[0].StringValue : null;
}
public static string GetDisplayText(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the DisplayText attributes
DisplayTextAttribute[] attribs = fieldInfo.GetCustomAttributes(
typeof(DisplayTextAttribute), false) as DisplayTextAttribute[];
// Return the first if there was a match.
return attribs.Length > 0 ? attribs[0].DisplayText : value.ToString();
}
There is a custom EDI serializer that serializes based on the StringValue attributes like so:
StringBuilder sb = new StringBuilder();
sb.Append(car.RoofStyle.GetStringValue());
sb.Append(car.TireSize.GetStringValue());
sb.Append(car.Model.GetStringValue());
...
There is another method that can get Enum Value from StringValue for Deserialization:
car.RoofStyle = Enums.GetCode<RoofStyle>(EDIString.Substring(4, 3))
Defined as:
public static class Enums
{
public static T GetCode<T>(string value)
{
foreach (object o in System.Enum.GetValues(typeof(T)))
{
if (((Enum)o).GetStringValue() == value.ToUpper())
return (T)o;
}
throw new ArgumentException("No code exists for type " + typeof(T).ToString() + " corresponding to value of " + value);
}
}
And Finally, for the UI, the GetDisplayText() is used to show the user friendly text.
What do you think? Overkill? Is there a better way? or Goldie Locks (just right)?
Just want to get feedback before I intergrate it into my personal framework permanently. Thanks.
The part you’re using to serialize is fine. The deserialization part is awkwardly written. The main problem is that you’re using
ToUpper()to compare strings, which is easily broken (think globalization). Such comparisons should be done withstring.Compareinstead, or the string.Equals overload that takes aStringComparison.The other thing is that performing these lookups again and again during deserialization is going to pretty slow. If you’re serializing a lot of data, this could actually be quite noticeable. In that case, you’d want to build a map from the
StringValueto the enum itself – throw it into a staticDictionary<string, RoofStyle>and use it as a lookup for the round-trip. In other words:It’s not as “generic” but it’s way more efficient. If you don’t like the duplication of string values then extract the codes as constants in a separate class.
If you really need it to be totally generic and work for any enum, you can always lazy-load the dictionary of values (generate it using the extension methods you’ve written) the first time a conversion is done. It’s very simple to do that:
P.S. Minor detail but you might want to consider using
Attribute.GetCustomAttributeinstead ofMemberInfo.GetCustomAttributesif you only expect there to be one attribute. There’s no reason for all the array fiddling when you only need one item.