Gegen Internetzensur
Stoppt die Vorratsdatenspeicherung! Jetzt klicken & handeln!Willst du auch bei der Aktion teilnehmen? Hier findest du alle relevanten Infos und Materialien:
  • Flickr Photos

    www.flickr.com
16. Dez
Sam

XmlEnumAttribute extrahieren

Vorwort

Ungefähr gestern früh gegen drei war es, dass ich an die Grenzen des .Net 2.0 XmlSerializers gestoßen bin. Dieser hatte sich vehement der Aufgabe verweigert, eine statische Klasse zu serialisieren, deren Inhalt ich auf der Platte speichern wollte.
(Nicht genug, dass eben diese Klasse auch eine Hashtable beinhaltete, welche allein einen Workaround für die Serialisierung benötigt hätte.)

Also entschloss ich mich, den steinigeren Weg zu gehen und die Klasse von Hand, also per XmlWriter auf die Platte zu jagen, um sie, auf dem umgekehrten Weg, via XmlReader wieder einzulesen.

Das klappte entgegen meiner anfänglichen Erwartungen erstaunlich gut, und vor allem: Einfach (vom Lesen einmal abgesehen) – Ein kleines Hindernis blieb aber doch. Während es vorher noch möglich war, die Klassen und ihre Elemente mit Metadaten zu taggen, um ihnen in der XML-Datei ein entsprechendes Outfit zu verpassen, war dem jetzt nur noch bedingt der Fall: Taggen ja, Outfit nein.

Ein Problem stellte das bezüglich der zu serialisierenden Klasse natürlich nicht dar – Was mir aber ein Bein stellte war, dass die Elemente der Hashtable ihrerseits eine Enumeration umfassten, deren Werte ich via XmlEnumAttribute fröhlich formatiert hatte – irgendwie umsonst. Damit blieb mir die Wahl zwischen “rohem” Wert – namentlich 0, 1, 2 usw. bis zur Oberkante – oder dem internen Namen des Wertes – Dinge wie “Rot”, “Blau”, “Gemüsebrühe” (bzw. “Gemuesebruehe” für die Genießer unter uns) oder was auch immer.

Ersteres ließe sich auf dem Rückweg via einfachem cast der Marke

  1. enum Utensil : int {
  2.    Bürste = 1,
  3.    Gabel = 2,
  4.    // …
  5.    Zahnstocher = 17,
  6. }
  7. // …
  8. string xmlValue = /* … XmlReader … –> */ "17";
  9. IrgendeineEnum value = (IrgendeineEnum) Int32.Parse( xmlValue );

zurechtbiegen, und zweiteres wäre auch nicht weiter schwierig wiederherzustellen, unter Zuhilfename der Enum-Funktion Parse à la

  1. string xmlValue = /* … XmlReader … –> */ "Zahnstocher";
  2. IrgendeineEnum value = (IrgendeineEnum) Enum.Parse(typeof (IrgendeineEnum), xmlValue);

Logisch aber, dass erstere Art der Speicherung nicht zweckmäßig im Sinne der Verständlichkeit des serialisierten Formates ist (“17” versus “Zahnstocher“) und zweiteres keine Option sein kann, wenn man die Anwendung lokalisiert oder, schlimmer, irgendwann einmal den Quellcode ändert (Gott bewahre).

Damit stellte sich also die Frage, wie man an das XmlEnum-Attribut eines beliebigen, gegebenen Enumerationswertes herankommt, um dieses zur Serialisierung zu verwenden – und wie man, andersherum, aus eben jenem String wieder den ursprünglichen Enumerationswert erhält. Lange Rede, kurzer Sinn: Hier ist die Lösung.

Lösung

Erhalten des XmlEnum-Attribut eines Enumerationswertes

Ein Wert einer Enumeration heißt in .NET-Sprache Feld. Folglich holen wir uns die Typinformationen der Enumeration und durchwandern alle ihre Felder, bis der Wert des Feldes mit dem gegebenen übereinstimmen. Danach besorgen wir uns die benutzerdefinierten Attribute des Feldes und picken das XmlEnumAttribute heraus.

  1. public static XmlEnumAttribute GetXmlEnumAttribute(object value)
  2. {
  3.         if (!(value is Enum)) throw new ArgumentException("enumeration value expected", "value");
  4.         Type type = value.GetType();
  5.        
  6.         // Iterate through all the fields of the class.
  7.         foreach (FieldInfo field in type.GetFields())
  8.         {
  9.                 // skip every field that is not of the same value
  10.                 object checkValue = field.GetValue(value);
  11.                 if (!Enum.Equals(checkValue, value)) continue;
  12.  
  13.                 // Find the XmlEnumAttribute and return it
  14.                 foreach (XmlEnumAttribute attr in Attribute.GetCustomAttributes(field))
  15.                         return attr;
  16.         }
  17.        
  18.         // return nothing
  19.         return null;
  20. }

Erhalten des Enumerationswertes anhand des XmlEnum-Attributs

Der umgekehrte Weg funktioniert ähnlich: Wir borgen uns die Typinfo, und durchlaufen alle Felder. In jedem Feld sehen wir uns die XmlEnum-Attribute an und finden eventuell das gesuchte. In diesem Fall geben wir den ‘rohen’ Wert des Feldes zurück. Dieser muss letztlich noch in die Enumeration gecastet werden und die Welt ist wieder schön:

  1. public static object GetValueFromXmlEnumAttribute(Type enumType, XmlEnumAttribute value)
  2. {
  3.         if (!enumType.IsEnum) throw new ArgumentException("enumType must describe an Enum", "enumType");
  4.        
  5.         // Iterate through all the fields of the class.
  6.         foreach (FieldInfo field in type.GetFields())
  7.         {
  8.                 // Iterate through all the XmlEnumAttributes (that is, find it)
  9.                 foreach (XmlEnumAttribute attr in Attribute.GetCustomAttributes(field))
  10.                 {
  11.                         if (attr.Equals(value)) return field.GetRawConstantValue();
  12.                 }
  13.         }
  14.         // return nothing
  15.         return null;
  16. }

Ein Beispiel zur Anwendung

  1. enum TestEnum : int
  2. {
  3.         [XmlEnum("-")]
  4.         Negativ = -1,
  5.         [XmlEnum("n/a")]
  6.         Neutral = 0,
  7.         [XmlEnum("+")]
  8.         Positiv = 1
  9. }
  10.  
  11. // …
  12.  
  13. XmlEnumAttribute attrib = GetXmlEnumAttribute(TestEnum.Positiv);
  14.         // attrib.Name == "+"
  15.  
  16. TestEnum value = (TestEnum)GetValueFromXmlEnumAttribute(typeof(TestEnum), new XmlEnumAttribute("-"));
  17.         // value == TestEnum.Negativ

Und wieder ein Problem weniger.

Hinterlasse eine Antwort

Folgende XHTML-Tags sind erlaubt: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>