You have a couple methods to solve this problem.
Firstly, you can use reflection to cycle through all the enum values using typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static)
similarly to how it is done in this this answer:
public static partial class XmlEnumExtensions
{
public static TEnum FromReflectedXmlValue<TEnum>(this string xml) where TEnum : struct, IConvertible, IFormattable
{
// Adapted from https://stackoverflow.com/questions/35294530/c-sharp-getting-all-enums-value-by-attribute
var obj = (from field in typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static)
from attr in field.GetCustomAttributes<XmlEnumAttribute>()
where attr != null && attr.Name == xml
select field.GetValue(null)).SingleOrDefault();
if (obj != null)
return (TEnum)obj;
// OK, maybe there is no XmlEnumAttribute override so match on the name.
return (TEnum)Enum.Parse(typeof(TEnum), xml, false);
}
}
Then use it like:
obj.ItsEnumValue = "01010101".FromReflectedXmlValue<myEnum>();
However, this will not work when the [Flags]
attribute is applied. For instance, given the following enum:
[Flags]
public enum myFlagsEnum
{
[System.Xml.Serialization.XmlEnumAttribute("Flag0")]
Zero = (1 << 0),
[System.Xml.Serialization.XmlEnumAttribute("Flag1")]
One = (1 << 1),
}
From the value myFlagsEnum.Zero | myFlagsEnum.One
, XmlSerializer
will generate the following combined string, which cannot be found purely by reflection: Flag0 Flag1
. Nor is it clear what should happen when multiple XmlEnumAttribute
attributes are applied to a given enum value, or only some of the enum values have XmlEnumAttribute
applied.
To handle all possible edge cases including the above, I would suggest simply deserializing the XML directly, using the following second method:
public static partial class XmlExtensions
{
static XmlExtensions()
{
noStandardNamespaces = new XmlSerializerNamespaces();
noStandardNamespaces.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd attributes.
}
readonly static XmlSerializerNamespaces noStandardNamespaces;
internal const string RootNamespace = "XmlExtensions";
internal const string RootName = "Root";
public static TEnum FromXmlValue<TEnum>(this string xml) where TEnum : struct, IConvertible, IFormattable
{
var element = new XElement(XName.Get(RootName, RootNamespace), xml);
return element.Deserialize<XmlExtensionsEnumWrapper<TEnum>>().Value;
}
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer = null)
{
using (var reader = element.CreateReader())
{
object result = (serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
if (result is T)
return (T)result;
}
return default(T);
}
public static string ToXmlValue<TEnum>(this TEnum value) where TEnum : struct, IConvertible, IFormattable
{
var root = new XmlExtensionsEnumWrapper<TEnum> { Value = value };
return root.SerializeToXElement().Value;
}
public static XElement SerializeToXElement<T>(this T obj)
{
return obj.SerializeToXElement(null, noStandardNamespaces); // Disable the xmlns:xsi and xmlns:xsd attributes by default.
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, XmlSerializerNamespaces ns)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
(serializer ?? new XmlSerializer(obj.GetType())).Serialize(writer, obj, ns);
var element = doc.Root;
if (element != null)
element.Remove();
return element;
}
}
[XmlRoot(XmlExtensions.RootName, Namespace = XmlExtensions.RootNamespace)]
[XmlType(IncludeInSchema = false)]
public class XmlExtensionsEnumWrapper<TEnum>
{
[XmlText]
public TEnum Value { get; set; }
}
This guarantees compatibility with XmlSerializer
in all cases - by actually using it. Then similarly do:
obj.ItsEnumValue = "01010101".FromXmlValue<myEnum>();
Sample fiddle.