2011-06-09 26 views
9

Necesito analizar un archivo XML que obtengo de terceros en objetos C#. Algunos de los XML que recibo tienen valores de enumeración que quiero almacenar en un tipo de enumeración.Asignación de valores xml a enum tipo

Por ejemplo, tengo la siguiente xsd del archivo xml:

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

que desea asignar a esta una enumeración y yo llegado hasta aquí:

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

El problema que tener es que el xml puede contener un valor de 3 que no puedo poner en el tipo enum. ¿Hay alguna solución para poner este valor en la enumeración?

También puedo obtener otros valores con un - o un / en ellos y que quiero poner en un tipo de enumeración.
Anu sugerencias son bienvenidas!

+0

que siga el enlace de abajo y me ha resultado útil. [En C#, deserializando enumeraciones de enteros.] (Http://stackoverflow.com/questions/9944421/c-sharp-deserializing-enums-from-integers/33387055#33387055) –

Respuesta

15

puede analizar el valor del atributo xml de nuevo a un tipo de enumeración con:

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

Pero no creo que obtendrá muy lejos con sus valores "especiales" (3, a/ etc.). ¿Por qué no definir su enumeración como

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

y escribir un método estático para convertir una cadena a un miembro de la enumeración?

Una cosa que podría considerar para implementar dicho método sería agregar atributos personalizados a los miembros enum que contienen su representación de cadena - si un valor no tiene una contraparte exacta en la enumeración, busque un miembro con la atributo.

Creación de un atributo tal es fácil:

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

y luego se puede utilizar en su enumeración:

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

Estos dos métodos le ayudarán a analizar una cadena en un miembro de la enumeración de su elección:

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

Y mientras estoy en ello: aquí es al revés;)

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

    } 
+2

El problema es mapear valores de enum no estándar (como "3"). –

+0

Gracias por la respuesta. ¡Probaré esto y te llamaré! – Gerard

+1

¡Funcionó, gracias por la respuesta detallada! – Gerard

0

he creado una clase que se encarga de esto:

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

Usted tiene que leer el archivo XML primero y poner los valores en una lista, puede hacer esto con el objeto XElement por ejemplo.

// Edición: De esta manera:

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); 
+0

espera, este no era el problema. ¿No @ funciona? como @ 3, @ /, etc. Sé que funciona con palabras clave como int y double. – hcb

3

Por qué no puedes analizar la cadena

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

Así que realmente serializa como una cadena.

11

Decorar con el atributo XmlEnum: 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 
} 
+2

He estado desgarrando mi cabeza para entender por qué no podía marcar campos de clase ya que [XmlEnum] finalmente encontré tu respuesta y me ahorré el resto de mi cabello. todas las otras alternativas son "sucias" si me preguntas –