Charles Bretana
Charles Bretana

Reputation: 146559

How to represent composite Flagged Enum values in XML config file

If I have a flagged enum lets say for argument

[Flags]
public enum Shipping { Null=0x00, FedX=0x01, UPS=0x02, AirBrn=0x04, USPS=0x08, Any=0xFF}

and I want to store this value in an Xml config file, such as

  [XmlAttribute(AttributeName="shipper")]
  public Shipping ShippingOption { get; set; }

I know that I have to separate combined values with a space, for example, If I want the config file to represent ShippingOption as either FedX or UPS, then the Xml would look like this:

  <ElementName shipper="FedX UPS" />

or if I want it to represent FedX or UPS or USPS then

  <ElementName shipper="FedX UPS USPS" />

But what do I put in the attribute if I want it to be anything but UPS? (Assuming the enum does not have a predefined value for this (like Shipping.Any) This would be the equivalent of c# value

       var ship = Shipping.Any & ^Shipping.UPS;

I know I could just leave out the one I don't want, like this:

 <ElementName shipper="FedX USPS AirBrn " /> 

But if there are many many members in the Enum that gets tedious, (and would require maintenance anytime the complete list expanded). Is there a short hand way to represent Negation of a flagged Enum ?

Upvotes: 1

Views: 822

Answers (1)

p.s.w.g
p.s.w.g

Reputation: 149040

Here's a fairly crude solution that should work. First, write a method to parse your custom enum syntax—in my implementation, ~X means '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;
}

And here's a very simple format function that's compatible with the original XML serialization:

private string FormatEnum<T>(T s) where T : struct, IConvertible
{
    return s.ToString().Replace(",", "");
}

Now just set up a proxy property wherever you need to use this and make sure you mark the original with XmlIgnore:

[XmlIgnore]
public Shipping ShippingOption { get; set; }

[XmlAttribute(AttributeName = "shipper")]
public string ShippingOptionString
{
    get { return FormatEnum<Shipping>ShippingOption); }
    set { ShippingOption = ParseEnum<Shipping>(value); }
}

Now it will be able to correctly parse XML like this:

<ElementName shipper="Any ~UPS" />

Other solutions exist for different serialization libraries, but the pattern would basically be the same. Deserialize a the XML attribute as string, then parse it using your custom parser to get the enum result you want.

Upvotes: 1

Related Questions