简体   繁体   中英

Mapping xml values to enum type

I need to parse a XML file which I get from third party to C# objects. Some of the XML I receive have enumeration values which I want to store in an enum type.

For example, I've got the following xsd of the xml file:

<xsd:simpleType name="brandstof">
  <xsd:restriction base="xsd:string">
    <!--  Benzine --> 
    <xsd:enumeration value="B" /> 
    <!--  Diesel --> 
    <xsd:enumeration value="D" /> 
    <!--  LPG/Gas --> 
    <xsd:enumeration value="L" /> 
    <!--  LPG G3 --> 
    <xsd:enumeration value="3" /> 
    <!--  Elektrisch --> 
    <xsd:enumeration value="E" /> 
    <!--  Hybride --> 
    <xsd:enumeration value="H" /> 
    <!--  Cryogeen --> 
    <xsd:enumeration value="C" /> 
    <!--  Overig --> 
    <xsd:enumeration value="O" /> 
  </xsd:restriction>
</xsd:simpleType>  

I want to map this to an enum and I got this far:

public enum Fuel
{
    B,
    D,
    L,
    E,
    H,
    C,
    O
}

The problem I have is that the xml can contain a value of 3 which I can't seem to put in the enum type. Is there any solution to put this value in the enum.

I also can get other values with a - or a / in them and which I want to put in an enum type.
Anu suggestions are welcome!

Decorate with the XmlEnum attribute: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlenumattribute.aspx

public enum Fuel
{
    [XmlEnum("B")]
    Benzine,
    [XmlEnum("D")]
    Diesel,
    [XmlEnum("L")]
    LpgGas,
    [XmlEnum("3")]
    LpgG3,
    [XmlEnum("E")]
    Elektrisch,
    [XmlEnum("H")]
    Hybride,
    [XmlEnum("C")]
    Cryogeen,
    [XmlEnum("O")]
    Overig
}

You can parse the xml attribute value back to an enum type with:

var value = Enum.Parse(typeof(Fuel), "B");

But I don't think you will get really far with your "special" values ( 3 , a/ etc.). Why don't you define your enum as

enum Fuel
{
    Benzine,
    Diesel,
    // ...
    Three,
    ASlash,
    // ...
}

And write a static method to convert a string to an enum member?

One thing you could look into for implementing such a method would be to add custom attributes to the enum members containing their string representation - if a value doesn't have an exact counterpart in the enumeration, look for a member with the attribute.

Creating such an attribute is easy:

/// <summary>
/// Simple attribute class for storing String Values
/// </summary>
public class StringValueAttribute : Attribute
{
    public string Value { get; private set; }

    public StringValueAttribute(string value)
    {
        Value = value;
    }
}

And then you can use them in your enum:

enum Fuel
{
    [StringValue("B")]        
    Benzine,
    [StringValue("D")]
    Diesel,
    // ...
    [StringValue("3")]
    Three,
    [StringValue("/")]
    Slash,
    // ...
}

These two methods will help you parse a string into an enum member of your choice:

    /// <summary>
    /// Parses the supplied enum and string value to find an associated enum value (case sensitive).
    /// </summary>
    public static object Parse(Type type, string stringValue)
    {
        return Parse(type, stringValue, false);
    }

    /// <summary>
    /// Parses the supplied enum and string value to find an associated enum value.
    /// </summary>
    public static object Parse(Type type, string stringValue, bool ignoreCase)
    {
        object output = null;
        string enumStringValue = null;

        if (!type.IsEnum)
        {
            throw new ArgumentException(String.Format("Supplied type must be an Enum.  Type was {0}", type));
        }

        //Look for our string value associated with fields in this enum
        foreach (FieldInfo fi in type.GetFields())
        {
            //Check for our custom attribute
            var attrs = fi.GetCustomAttributes(typeof (StringValueAttribute), false) as StringValueAttribute[];
            if (attrs != null && attrs.Length > 0)
            {
                enumStringValue = attrs[0].Value;
            }                       

            //Check for equality then select actual enum value.
            if (string.Compare(enumStringValue, stringValue, ignoreCase) == 0)
            {
                output = Enum.Parse(type, fi.Name);
                break;
            }
        }

        return output;
    }

And while I'm at it: here is the other way round;)

    /// <summary>
    /// Gets a string value for a particular enum value.
    /// </summary>
    public static string GetStringValue(Enum value)
    {
        string output = null;
        Type type = value.GetType();

        if (StringValues.ContainsKey(value))
        {
            output = ((StringValueAttribute) StringValues[value]).Value;
        }
        else
        {
            //Look for our 'StringValueAttribute' in the field's custom attributes
            FieldInfo fi = type.GetField(value.ToString());
            var attributes = fi.GetCustomAttributes(typeof(StringValueAttribute), false);
            if (attributes.Length > 0)
            {
                var attribute = (StringValueAttribute) attributes[0];
                StringValues.Add(value, attribute);
                output = attribute.Value;
            }

        }
        return output;

    }

Why can't you parse the string

[XmlAttribute("brandstof")]
public string FuelTypeString
{
    get { return fuel.ToString(); }
    set
    {
        fuel = (Fuel)System.Enum.Parse(typeof(Fuel), value);
    }
}
[XmlIgnore()]
public Fuel FuelType
{
    get { return fuel; }
    set { fuel = value; }
}

So you really serialize it as a string.

I created a class which handles this:

    class EnumCreator
    {
        public static Type CreateEnum(List<string> AValues)
        {
            // Get the current application domain for the current thread.
            AppDomain currentDomain = AppDomain.CurrentDomain;

            // Create a dynamic assembly in the current application domain, 
            // and allow it to be executed and saved to disk.
            AssemblyName aName = new AssemblyName("TempAssembly");
            AssemblyBuilder ab = currentDomain.DefineDynamicAssembly(
                aName, AssemblyBuilderAccess.RunAndSave);

            // Define a dynamic module in "TempAssembly" assembly. For a single-
            // module assembly, the module has the same name as the assembly.
            ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");      

            // Define a public enumeration with the name "Elevation" and an 
            // underlying type of Integer.
            EnumBuilder eb = mb.DefineEnum("EnumValues", TypeAttributes.Public, typeof(int));

            // Define two members, "High" and "Low".
            foreach (string v in AValues)
            {
                eb.DefineLiteral(v, AValues.IndexOf(v));
            }

            // Create the type and save the assembly.
            Type finished = eb.CreateType();

            return finished;
        }
    }

You do have to read the xml file first and put the values in a list, you can do this with the XElement object for example.

//Edit: Like this:

XElement xml = XElement.parse("file.xml");
List<string> enumItems = new List<string>();

foreach(XElement row in   xml.Element("xsd:simpleType").Element("xsd:restriction").Elements())
{
  enumItems.Add(row.Attribute("value").Value);
}

Type Fuel = EnumCreator.CreatEnum(enumItems);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM