2011-06-09 13 views
9

Ho bisogno di analizzare un file XML che ottengo da oggetti di terze parti a C#. Alcuni dei XML che ricevo hanno valori di enumerazione che voglio memorizzare in un tipo enum.Mappatura dei valori xml al tipo enum

Per esempio, ho il seguente XSD del file 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> 

voglio mappare questo per un enum e ho ottenuto fino a questo punto:

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

Il problema che è che il xml può contenere un valore di 3 che non riesco a mettere nel tipo di enum. C'è qualche soluzione per mettere questo valore nell'enum.

Posso anche ottenere altri valori con uno - o uno / e che voglio inserire in un tipo enum.
Anu suggerimenti sono i benvenuti!

+0

seguo sul link qui sotto e lo trovo utile. [In C#, deserializzazione delle enumerazioni dagli interi.] (Http://stackoverflow.com/questions/9944421/c-sharp-deserializing-enums-from-integers/33387055#33387055) –

risposta

15

è possibile analizzare il valore di attributo XML di nuovo ad un tipo enum con:

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

Ma non credo che si otterrà davvero lontano con i propri valori "speciali" (3, a/ ecc). Perché non si fa a definire il tuo enum come

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

e scriviamo un metodo statico per convertire una stringa in un membro enum?

Una cosa che è possibile esaminare per l'implementazione di tale metodo consiste nell'aggiungere attributi personalizzati ai membri dell'enumerazione contenente la loro rappresentazione di stringa: se un valore non ha una controparte esatta nell'enumerazione, cercare un membro con attributo.

La creazione di un tale attributo è facile:

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

E poi si possono usare nel vostro enum:

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

Questi due metodi vi aiuterà analizzare una stringa in un membro enum della vostra scelta:

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

E mentre io sono a esso: qui è il contrario;)

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

Il problema è la mappatura dei valori di enum non standard (come "3"). –

+0

Grazie per la risposta. Ci proverò e tornerò da te! – Gerard

+1

Ha funzionato, grazie per la risposta dettagliata! – Gerard

0

ho creato una classe che gestisce questo:

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

Dovete leggere il file XML prima e mettere i valori in un elenco, si può fare questo con l'oggetto XElement per esempio.

// Edit: Ti piace questa:

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

attendere, questo non era il problema. Non funziona @? come @ 3, @ /, ecc. so che funziona con parole chiave come int e double. – hcb

3

Perché non si può analizzare la stringa

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

Così realmente serializzare come una stringa.

11

Decorare con l'attributo 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

Mi sono stancato la testa per capire perché non potevo contrassegnare i campi della classe come [XmlEnum] alla fine ho trovato la tua risposta e mi sono salvato il resto dei miei capelli. tutte le altre alternative sono "sporche" se me lo chiedi –