【问题标题】:Get element of an enum by sending XmlEnumAttribute c#?通过发送 XmlEnumAttribute c# 获取枚举的元素?
【发布时间】:2017-03-25 06:02:04
【问题描述】:

我通常不必问问题,因为大多数时候我在互联网上找到我需要的东西,但现在我还没有找到方法:

想象一下,我在这个枚举中有 50000 个枚举元素:

public enum myEnum
{
   [System.Xml.Serialization.XmlEnumAttribute("01010101")]
   Item01010101,
   [System.Xml.Serialization.XmlEnumAttribute("10101500")]
   Item10101500
}

我要做的是通过传递元素的 XmlEnumAttribute 的字符串值来获取元素值,例如

object.ItsEnumValue = getEnumElement("01010101");

// Function which will return the element of the enum.
public myEnum getEnumElement(string xmlAttributeValue)
{
    // The function should return the enum element, not using a switch statement
    return myEnum.Item01010101;
}

有没有办法在没有 switch 语句的情况下做到这一点? 希望您能帮帮我,谢谢。

【问题讨论】:

  • 我在该示例中看到的是他们正在尝试获取枚举元素的描述,我需要获取的是元素的值,但我需要它自己的元素。你能帮帮我吗?

标签: c# xml enums


【解决方案1】:

你有几种方法可以解决这个问题。

首先,您可以使用反射通过typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static) 循环遍历所有枚举值,类似于在此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);
    }
}

然后像这样使用它:

obj.ItsEnumValue = "01010101".FromReflectedXmlValue<myEnum>();

但是,当应用 [Flags] 属性时,这将不起作用。例如,给定以下枚举:

[Flags]
public enum myFlagsEnum
{
    [System.Xml.Serialization.XmlEnumAttribute("Flag0")]
    Zero = (1 << 0),
    [System.Xml.Serialization.XmlEnumAttribute("Flag1")]
    One = (1 << 1),
}

myFlagsEnum.Zero | myFlagsEnum.One的值,XmlSerializer会生成如下组合字符串,纯反射是找不到的:Flag0 Flag1。也不清楚当多个XmlEnumAttribute 属性应用于给定枚举值时会发生什么,或者只有一些枚举值应用了XmlEnumAttribute

要处理所有可能的边缘情况,包括上述情况,我建议直接反序列化 XML,使用以下第二种方法:

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; }
}

这保证了在所有情况下都与XmlSerializer 兼容 - 通过实际使用它。然后同样做:

obj.ItsEnumValue = "01010101".FromXmlValue<myEnum>();

示例fiddle

【讨论】:

    【解决方案2】:

    感谢 dbc,第一种方法非常适合我需要做的事情,:) 非常感谢您的帮助,我创建了这个类:

    public static partial class XmlEnumExtensions
    {
        public static Nullable<TEnum> FromReflectedXmlValue<TEnum>(this string xml) where TEnum : struct, IConvertible, IFormattable
        {
            try
            {
                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);
    
            }
            catch (ArgumentException ex)
            {
                throw new ApplicationException("Error: " + ex.Message);
            }
        }
    }
    

    然后我像这样调用 FromReflectedXmlValue 方法:

    var obj4 = XmlEnumExtensions.FromReflectedXmlValue<myEnum>("theXmlAtributeValue");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-04
      • 1970-01-01
      • 1970-01-01
      • 2011-03-05
      相关资源
      最近更新 更多