如何在 XML 配置文件中表示复合标记枚举值
How to represent composite Flagged Enum values in XML config file
如果我有一个标记的枚举让我们说参数
[Flags]
public enum Shipping { Null=0x00, FedX=0x01, UPS=0x02, AirBrn=0x04, USPS=0x08, Any=0xFF}
我想将此值存储在 Xml 配置文件中,例如
[XmlAttribute(AttributeName="shipper")]
public Shipping ShippingOption { get; set; }
我知道我必须用 space 分隔组合值,例如,如果我希望配置文件将 ShippingOption 表示为 FedX 或 UPS,那么 Xml 看起来像这个:
<ElementName shipper="FedX UPS" />
或者如果我想让它代表 FedX 或 UPS 或 USPS 那么
<ElementName shipper="FedX UPS USPS" />
但是如果我希望它不是 UPS,我应该在属性中输入什么?
(假设枚举没有为此预定义的值(如 Shipping.Any
)
这相当于 c# value
var ship = Shipping.Any & ^Shipping.UPS;
我知道我可以忽略我不想要的那个,像这样:
<ElementName shipper="FedX USPS AirBrn " />
但是如果 Enum 中有很多成员会变得乏味,(并且在完整列表展开时需要随时维护)。是否有一种简写方式来表示已标记枚举的否定?
这是一个应该可行的相当粗略的解决方案。首先,编写一个方法来解析您的自定义枚举语法——在我的实现中,~X
表示 'and not X
':
private T ParseEnum<T>(string str) where T : struct, IConvertible
{
var result = 0;
foreach (var name in str.Split())
{
if (name.StartsWith("~"))
{
result &= ~(int)(Enum.Parse(typeof(T), name.Substring(1), true));
}
else
{
result |= (int)(Enum.Parse(typeof(T), name, true));
}
}
return (T)(object)result;
}
这里有一个非常简单的格式函数,它与原始的XML序列化兼容:
private string FormatEnum<T>(T s) where T : struct, IConvertible
{
return s.ToString().Replace(",", "");
}
现在只需在需要使用它的任何地方设置代理 属性 并确保将原始标记为 XmlIgnore
:
[XmlIgnore]
public Shipping ShippingOption { get; set; }
[XmlAttribute(AttributeName = "shipper")]
public string ShippingOptionString
{
get { return FormatEnum<Shipping>ShippingOption); }
set { ShippingOption = ParseEnum<Shipping>(value); }
}
现在它将能够像这样正确解析 XML:
<ElementName shipper="Any ~UPS" />
针对不同的序列化库存在其他解决方案,但模式基本相同。将 XML 属性反序列化为字符串,然后使用自定义解析器解析它以获得所需的枚举结果。
如果我有一个标记的枚举让我们说参数
[Flags]
public enum Shipping { Null=0x00, FedX=0x01, UPS=0x02, AirBrn=0x04, USPS=0x08, Any=0xFF}
我想将此值存储在 Xml 配置文件中,例如
[XmlAttribute(AttributeName="shipper")]
public Shipping ShippingOption { get; set; }
我知道我必须用 space 分隔组合值,例如,如果我希望配置文件将 ShippingOption 表示为 FedX 或 UPS,那么 Xml 看起来像这个:
<ElementName shipper="FedX UPS" />
或者如果我想让它代表 FedX 或 UPS 或 USPS 那么
<ElementName shipper="FedX UPS USPS" />
但是如果我希望它不是 UPS,我应该在属性中输入什么?
(假设枚举没有为此预定义的值(如 Shipping.Any
)
这相当于 c# value
var ship = Shipping.Any & ^Shipping.UPS;
我知道我可以忽略我不想要的那个,像这样:
<ElementName shipper="FedX USPS AirBrn " />
但是如果 Enum 中有很多成员会变得乏味,(并且在完整列表展开时需要随时维护)。是否有一种简写方式来表示已标记枚举的否定?
这是一个应该可行的相当粗略的解决方案。首先,编写一个方法来解析您的自定义枚举语法——在我的实现中,~X
表示 'and not X
':
private T ParseEnum<T>(string str) where T : struct, IConvertible
{
var result = 0;
foreach (var name in str.Split())
{
if (name.StartsWith("~"))
{
result &= ~(int)(Enum.Parse(typeof(T), name.Substring(1), true));
}
else
{
result |= (int)(Enum.Parse(typeof(T), name, true));
}
}
return (T)(object)result;
}
这里有一个非常简单的格式函数,它与原始的XML序列化兼容:
private string FormatEnum<T>(T s) where T : struct, IConvertible
{
return s.ToString().Replace(",", "");
}
现在只需在需要使用它的任何地方设置代理 属性 并确保将原始标记为 XmlIgnore
:
[XmlIgnore]
public Shipping ShippingOption { get; set; }
[XmlAttribute(AttributeName = "shipper")]
public string ShippingOptionString
{
get { return FormatEnum<Shipping>ShippingOption); }
set { ShippingOption = ParseEnum<Shipping>(value); }
}
现在它将能够像这样正确解析 XML:
<ElementName shipper="Any ~UPS" />
针对不同的序列化库存在其他解决方案,但模式基本相同。将 XML 属性反序列化为字符串,然后使用自定义解析器解析它以获得所需的枚举结果。