XmlExporter vs. XmlSerializer

Während der Arbeit an einem Konfigurationstool, kam die Frage auf, wie eine relativ umfangreiche verschachtelte Struktur möglichst einfach nach XML exportiert werden kann. Die zu exportierende Struktur liegt im Speicher bereits mit diversen Werten ausgefüllt, in Form von entsprechend verschachtelten Klassen vor. Dabei sollen alle öffentlichen Felder (einschließlich Arrays) in das XML-Dokument übernommen werden.

Die simpelste Möglichkeit ist es, den XmlSerializer vom .NET Framework zu verwenden.

[Serializable]
public class SubStructure {
    public int value2;
    public SubStructure() {
        value2 = 2;
    }
}

[Serializable]
public class Structure {
    public int value1;
    public SubStructure sub;
    public SubStructure[] arr;

    public Structure() { 
        value1 = 1 ;
        sub = new SubStructure();
         arr = new SubStructure[] {
            new SubStructure(),
             new SubStructure()
        };
     }
}

Wird eine Instanz von der Klasse Structure mit Hilfe eines XmlSerialisers serialisiert sieht das Ergebnis wie folgendes Listing aus:

<?xml version="1.0" encoding="Windows-1252"?>
<Structure xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <value1>1</value1>
  <sub>
    <value2>2</value2>
  </sub>
  <arr>
    <SubStructure>
      <value2>2</value2>
    </SubStructure>
    <SubStructure>
      <value2>2</value2>
    </SubStructure>
  </arr>
</Structure>

Diese Struktur ist bereits XML-konform formatiert und kann bei Bedarf auf die gleiche Weise wieder eingelesen werden. Jedoch kann es unter Umständen erforderlich sein in den jeweligen Klassen Konstruktoren mit Parametern zu haben bzw. die Ausgabe etwas anders zu gestalten. Hier ist der “eingebaute” Serializer am Ende.

Eine einfache Möglichkeit diese Einschränkung zu umgehen besteht darin, in jeder Klasse die Methode ToXmlNode() hinzuzufügen. Um jedoch mit den Strukturen möglichst flexibel zu bleiben, wäre eine andere Möglichkeit eine Basis-Klasse mit generischer Implementierung.

Das folgende Listing zeigt eine abstrakte Basisklasse für die gewünschten Strukturen sowie eine Schnittstelle für alle von und nach XML-exportierbaren Klassen:

using System.Xml

public interface IXmlExportable {
   XmlNode ToXmlNode(XmlDocument doc);
   XmlNode ToXmlNode(string name, XmlDocument doc);
}
public abstract class XmlExportable : IXmlExportable {

   #region IXmlExportable Members

   public XmlNode ToXmlNode(XmlDocument doc) {
       return ToXmlNode(this.GetType().Name, doc);
   }

   public XmlNode ToXmlNode(string name, XmlDocument doc) {
       XmlNode ret = doc.CreateElement(name);
       foreach (FieldInfo fi in this.GetType().GetFields()) {
           if (fi.FieldType.BaseType == typeof(Array)) {
               Array arr = (Array)fi.GetValue(this);
               foreach (object obj in arr) {
                   AppendObject(doc, ret, fi.Name, obj);
               }
           } else {
               AppendObject(doc, ret, fi.Name, fi.GetValue(this));
           }
       }
       return ret;
   }

   private void AppendObject(XmlDocument doc, XmlNode ret, string name, object obj) {
       if (obj == null) {
           return;
       }
       XmlNode child;
       Type type = obj.GetType().GetInterface("IXmlExportable", false);
       if (type != null) {
           IXmlExportable iexp = (IXmlExportable)obj;
           child = iexp.ToXmlNode(name, doc);
       } else {
           child = doc.CreateElement(name);
           child.AppendChild(doc.CreateTextNode(obj.ToString()));
       }
       ret.AppendChild(child);
   }

   #endregion
}

Die auf diese Weise erzeugte XML-Datei entspricht somit genau der Anforderung des Projektes, die Strukturen (Klassen) nach XML zu exportieren. Auf diese Art lassen sich sehr komplexe XML-Dateien schnell zusammenbauen und ausgeben.

<Structure>
   <value1>1</value1>
   <sub>
      <value2>2</value2>
   </sub>
   <arr>
      <value2>2</value2>
   </arr>
   <arr>
      <value2>2</value2>
   </arr>
</Structure>

Fazit: Der Serializer des .NET Frameworks ist 100% XML-konform und ermöglicht ein sehr einfaches “wieder-einlesen” der erzeugten XML-Daten, ist jedoch was die Gestaltung der XML-Struktur angeht begrenzt, da diese lediglich mit entsprechenden Attributen beeinflusst werden kann.

Wird eine flexible “frei” gestaltete XML Struktur benötigt, ist ein selbst programmierter XMLExporter die bessere Wahl.