Hallo zusammen!
Ich versuche mich gerade an der Erstellung einer XML-Datei, die mehrere Namespaces enthält. Beim Schreiben dieser Datei sollen die Elemente und Attribute nun mit ihren voll qualifizierten Namen ausgegeben werden. Das funktioniert wie folgt leider nicht. In der geschriebenen Datei sind keine Präfixe enthalten.
XmlDocument xmlDoc = new XmlDocument();
XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "ISO-8859-1", null);
XmlElement rootNode = xmlDoc.CreateElement("a:root");
XmlElement test = xmlDoc.CreateElement("b:test");
xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement);
xmlDoc.AppendChild(rootNode);
rootNode.AppendChild(test);
xmlDoc.Save(@"C:\test.xml");
Ausgabe:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<root>
<test />
</root>
Ich möchte:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<a:root xmlns:a="url" xmlns:b="url" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="url">
<b:test />
</a:root>
Das Beispiel ist recht einfach. Mein eigentlicher Code ist etwas komplexer, aber vom Ansatz her gleich. Ich habe ein XmlDocument, füge Elemente hinzu und speichere es ab. Sämtliche Elemente und Attribute (inkl. dem Root-Element) sollen mit ihrem jeweiligen Präfix versehen werden.
Habe schon längere Zeit gesucht und rumprobiert (z.B. mit dem XmlNamespaceManager etc.), aber nichts passendes gefunden. Meine Alternative wäre ein simples Schreiben Element für Element per XmlWriter, aber das halte ich nicht für praktikabel. Da muss es doch eine einfachere Lösung geben!
Danke schonmal für die Hilfe und noch nen schönen Abend beim Fußballgucken 😉
Daedalus
Grrr... nach langem Suchen hatte ich nun keine Lust mehr und habe mir einfach ein kleines Workaround für das Problem überlegt.
War gar nicht so schwer. Mittels XmlTextWriter schreibe ich das XmlDocument Stück für Stück in die Datei. Vielleicht hilft's ja jemandem.
/// <summary>
/// Schreibt das angegebene XmlDocument in eine XML-Datei.
/// </summary>
/// <param name="xmlDoc">Das zu schreibende XmlDocument.</param>
private void SchreibeConfigDatei(XmlDocument xmlDoc)
{
XmlTextWriter writer = new XmlTextWriter(DateiName, Encoding.UTF8);
writer.WriteStartDocument();
foreach (XmlNode node in xmlDoc.ChildNodes)
{
if (node.LocalName != "xml")
{
this.SchreibeXmlNode(writer, node);
}
}
writer.WriteEndDocument();
writer.Close();
}
/// <summary>
/// Fügt das übergebene Element dem übergebenen XmlTextWriter hinzu.
/// Dabei werden alle Attribute und Kindelemente (mittels Rekursion)
/// mit ihren qualifizierten Namen ausgegeben.
/// </summary>
/// <param name="writer">XmlTextWriter, der das XmlDocument schreiben soll.</param>
/// <param name="node">Element, das dem XmlTextWriter hinzugefügt werden soll.</param>
private void SchreibeXmlNode(XmlTextWriter writer, XmlNode node)
{
writer.WriteStartElement(node.Prefix, node.LocalName, node.NamespaceURI);
if (node.Attributes != null)
{
foreach (XmlAttribute attr in node.Attributes)
{
writer.WriteAttributeString(attr.Prefix, attr.LocalName, attr.NamespaceURI, attr.Value.ToString());
}
}
if (node.HasChildNodes)
{
foreach (XmlNode childNode in node.ChildNodes)
{
if (childNode.NodeType != XmlNodeType.Text)
{
this.SchreibeXmlNode(writer, childNode);
}
else
{
writer.WriteString(childNode.InnerText);
}
}
}
writer.WriteEndElement();
}
Ja ich weiß das Thread wurde vor nem halben Jahr begonnen, aber ich möchte trozdem eben zeigen, wie man das macht, mit den Namespaces vor dem Tag.
Evtl. hilft es wem weiter...
Ich nutze für das kleine Beispiel die namespaces zum erstellen einer spreadsheetDrawing datei die in xlsx dateien notwendig sind.
(ich werde nicht auf die komplette datei eingehen, nur auf einzelne abschnitte)
Ich setze voraus, dass ihr mit euch einiger maaßen mit dem XmlSerializer auskennt.
Beispiel 1:
In der spreadsheetDrawing datei drawing1.xml wird vor jedem Knoten ein xdr: benötigt, damit es auch eindeutig zu excel zugeordnet wird:
dateiauschnitt:
<xdr:wsDr xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing">
<xdr:twoCellAnchor editAs="twoCell">
[...]
</xdr:twoCellAnchor>
</xdr:wsDr>
Ich gehe davon aus, das Ihr die objekte dafür schon angelegt/generiert habt.
Wichtig ist, dass die Objekte alle folgendes Attribut haben:
[XmlType(TypeName="#KnotenName#",Namespace=Declarations.SchemaVersion),Serializable]
#KnotenName# = der Name des Knotens. in diesem beispeil wäre das: wsDr
Declarations ist in diesem Fall eine Struktur und hat folgenden aufbau:
public struct Declarations {
public const string SchemaVersion = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
}
also nix tolles stattdessen, könnt ihr auch direkt den string reintippen.... nur wenn man mehere objekte mit dem Namespace hat, ist es angenehmer ne struktur zu haben, als jedesmal den namespace ein einzutippen.
So damit würde jetzt folgendes passieren wenn ich mein Objekt serialisieren würde:
<wsDr xmlns:xsi="http://schemas.openxmlformats.org/drawingml/2006/main">
<twoCellAnchor editAs="twoCell">
[...]
</twoCellAnchor>
</wsDr>
damit der Präfix zum Knoten hinzugefügt wird, müsst ihr den Serializer mit teilen, dass er die NS mit Präfixes ersetzen soll:
wsDr drawing = new wsDr();
drawing.twoCellAnchor = new twoCellAnchor();
drawing.twoCellAnchor.editAs = "oneCell";
[...]
XmlSerializer serializer = new XmlSerializer(drawing.GetType());
XmlSerializerNamespaces nsManager = new XmlSerializerNamespaces();
nsManager.Add("xdr", "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" );
StreamWriter sw = new StreamWriter(@"c:\test.xml");
serializer.Serialize(sw, drawing, nsManager);
sw.close();
Das ergebnis sieht folgender maßen aus:
<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing">
<xdr:twoCellAnchor editAs="twoCell">
[...]
</xdr:twoCellAnchor>
</xdr:wsDr>
Beispiel 2:
Hier gehen wir davon aus, dass wir mehrere Objekte mit vershiedenen Namespaces und auch ein Attribut, das einen anderen Namespace hat.
Unser Ziel sieht so aus:
<xdr:wsDr xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing">
<xdr:twoCellAnchor editAs="twoCell">
[...]
<xdr:pic macro="" fPublished="false">
[...]
<xdr:blipFill>
<a:blip r:embed="rId1" cstate="none" />
</xdr:blipFill>
</xdr:pic>
[...]
</xdr:towCellAnchor>
</xdr:wsDr>
ich gehe davon aus das die Objekte shcon angelegt sind.
Also setzen wir jetzt noch die Attribute
//Für wsDr
[XmlType(TypeName="wsDr",Namespace="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"),Serializable]
//Für twoCellAnchor
[XmlType(TypeName="twoCellAnchor",Namespace="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"),Serializable]
//Für pic
[XmlType(TypeName="pic",Namespace="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"),Serializable]
//Für blipFill
[XmlType(TypeName="bilpFill",Namespace="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"),Serializable]
//Für blip (achtung anderer namespace)
[XmlType(TypeName="blip",Namespace="http://schemas.openxmlformats.org/drawingml/2006/main"),Serializable]
//Für das Attribut embed
[XmlAttribute(AttributeName="embed",DataType="string",Namespace="http://schemas.openxmlformats.org/officeDocument/2006/relationships")]
Bitte vergesst nicht, dass XmlType für die klasse gesetzt wird und XmlAttribut für eine Eigenschaft.
so nun müssen wir nur noch dem Serializer sagen, welche Präfix er nutzen soll:
wsDr drawing = new wsDr();
[...]
XmlSerializer serializer = new XmlSerializer(drawing.GetType());
XmlSerializerNamespaces nsManager = new XmlSerializerNamespaces();
nsManager.Add("xdr", "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" );
nsManager.Add("a", "http://schemas.openxmlformats.org/drawingml/2006/main");
nsManager.Add("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
StreamWriter sw = new StreamWriter(@"c:\tester.xml");
serializer.Serialize( sw, drawing, nsManager);
sw.close();
und es ist vollbracht.
Ich hoffe die anleitung hilft einigen weiter, und ich hoffe auch dass sie zu verstehen ist.
LG
4.NET