Konvertieren von Sonderzeichen beim Schreiben von XML-Inhalten
Der XmlWriter beinhaltet die WriteRaw-Methode, mit der Sie unformatiertes Markup manuell schreiben können. Diese Methode verhindert das Schützen von Sonderzeichen. Im Gegensatz dazu schützt die WriteString-Methode bestimmte Zeichenfolgen in Form von entsprechenden Entitätsverweisen. Die Zeichen, die geschützt werden, sind in Abschnitt 2.4 Character Data and Markup und in Abschnitt 3.3.3 Attribute-Value Normalization der Empfehlung "Extensible Markup Language (XML) 1.0 (Fifth Edition)" beschrieben. Wenn die WriteString-Methode beim Schreiben eines Attributwerts aufgerufen wird, schützt sie ' und ". Die Zeichenwerte 0x-0x1F werden als numerische Zeichenentitäten � bis  codiert, mit Ausnahme der Leerraumzeichen 0x9, 0x10 und 0x13.
Die Richtlinie dafür, wann WriteString oder WritingRaw zu verwenden ist, lautet also wie folgt: WriteString wird verwendet, wenn beim Suchen nach Entitätszeichen jedes Zeichen durchlaufen werden muss, und WriteRaw schreibt genau das, was angegeben ist.
Die WriteNode-Methode kopiert alles aus dem aktuellen Knoten, und der Reader wird am Writer positioniert. Der Reader wird daraufhin zur weiteren Verarbeitung an den nächsten nebengeordneten Knoten weitergerückt. Mit der writeNode-Methode können Informationen auf einfache Weise aus einem Dokument in ein anderes extrahiert werden.
In der folgenden Tabelle werden die unterstützten NodeTypes für die WriteNode-Methode angezeigt.
Knotentyp |
Beschreibung |
---|---|
Element |
Schreibt den Elementknoten und alle Attributknoten. |
Attribut |
Keine Operation. Verwenden Sie WriteStartAttribute oder WriteAttributeString zum Schreiben des Attributs. |
Text |
Schreibt den Textknoten. |
CDATA |
Schreibt den CDATA-Abschnittsknoten. |
EntityReference |
Schreibt den Entity Ref-Knoten. |
ProcessingInstruction |
Schreibt den PI-Knoten. |
Comment |
Schreibt den Comment-Knoten. |
DocumentType |
Schreibt den DocType-Knoten. |
Whitespace |
Schreibt den Whitespace-Knoten. |
SignificantWhitespace |
Schreibt den Whitespace-Knoten. |
EndElement |
Keine Operation. |
EndEntity |
Keine Operation. |
Das folgende Codebeispiel veranschaulicht den Unterschied zwischen der WriteString-Methode und der WriteRaw-Methode, wenn das Kleiner-als-Zeichen ("<") angegeben wird. In diesem Codebeispiel wird WriteString verwendet.
w.WriteStartElement("myRoot")
w.WriteString("<")
w.WriteEndElement()
Dim tw As New XmlTextWriter(Console.Out)
tw.WriteDocType(name, pubid, sysid, subset)
w.WriteStartElement("myRoot");
w.WriteString("<");
w.WriteEndElement();
XmlTextWriter tw = new XmlTextWriter(Console.Out);
tw.WriteDocType(name, pubid, sysid, subset);
Ausgabe
<myRoot><</myRoot>
In diesem Codebeispiel wird WriteRaw verwendet, und die Ausgabe hat ein ungültiges Zeichen als Elementinhalt.
w.WriteStartElement("myRoot")
w.WriteRaw("<")
w.WriteEndElement()
w.WriteStartElement("myRoot");
w.WriteRaw("<");
w.WriteEndElement();
Ausgabe
<myRoot><</myRoot>
Im folgenden Codebeispiel wird veranschaulicht, wie ein XML-Dokument von einem elementbasierten in ein attributbasiertes Dokument konvertiert wird. Sie können auch eine umgekehrte Konvertierung (von einem attributbasierten in ein elementbasiertes Dokument) durchführen. Elementbasiert bedeutet, dass das XML-Dokument so konzipiert wurde, dass es viele Elemente, aber wenig Attribute enthält. Ein attributbasierter Entwurf enthält weniger Elemente, und das, was in einem elementbasierten Entwurf die Elemente wären, sind hier Attribute von Elementen. Es sind also weniger Elemente, aber mehr Attribute pro Element enthalten.
Dieses Codebeispiel ist nützlich, wenn Sie die XML-Daten in einem dieser beiden Modi entworfen haben, da sie dann in den jeweils anderen Modus konvertiert werden können.
Im folgenden XML-Code wird ein elementbasiertes Dokument verwendet. Die Elemente enthalten keine Attribute.
Input - centric.xml
<?xml version='1.0' encoding='UTF-8'?>
<root>
<Customer>
<firstname>Jerry</firstname>
<lastname>Larson</lastname>
<Order>
<OrderID>Ord-12345</OrderID>
<OrderDetail>
<Quantity>1301</Quantity>
<UnitPrice>$3000</UnitPrice>
<ProductName>Computer</ProductName>
</OrderDetail>
</Order>
</Customer>
</root>
Mit der folgenden Beispielanwendung wird die Konvertierung ausgeführt.
' The program will convert an element-centric document to an
' attribute-centric document or element-centric to attribute-centric.
Imports System
Imports System.Xml
Imports System.IO
Imports System.Text
Imports System.Collections
Class ModeConverter
Private bufferSize As Integer = 2048
Friend Class ElementNode
Private _name As [String]
Private _prefix As [String]
Private _namespace As [String]
Private _startElement As Boolean
Friend Sub New()
Me._name = Nothing
Me._prefix = Nothing
Me._namespace = Nothing
Me._startElement = False
End Sub 'New
Friend Sub New(prefix As [String], name As [String], [nameSpace] As [String])
Me._name = name
Me._prefix = prefix
Me._namespace = [nameSpace]
End Sub 'New
Public ReadOnly Property name() As [String]
Get
Return _name
End Get
End Property
Public ReadOnly Property prefix() As [String]
Get
Return _prefix
End Get
End Property
Public ReadOnly Property [nameSpace]() As [String]
Get
Return _namespace
End Get
End Property
Public Property startElement() As Boolean
Get
Return _startElement
End Get
Set
_startElement = value
End Set
End Property
End Class 'ElementNode
' Entry point which delegates to C-style main Private Function.
Public Overloads Shared Sub Main()
Main(System.Environment.GetCommandLineArgs())
End Sub
Overloads Public Shared Sub Main(args() As [String])
Dim modeConverter As New ModeConverter()
If args(0) Is Nothing Or args(0) = "?" Or args.Length < 2 Then
modeConverter.Usage()
Return
End If
Dim sourceFile As New FileStream(args(1), FileMode.Open, FileAccess.Read, FileShare.Read)
Dim targetFile As New FileStream(args(2), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)
If args(0) = "-a" Then
modeConverter.ConertToAttributeCentric(sourceFile, targetFile)
Else
modeConverter.ConertToElementCentric(sourceFile, targetFile)
End If
Return
End Sub 'Main
Public Sub Usage()
Console.WriteLine("? This help message " + ControlChars.Lf)
Console.WriteLine("Convert -mode sourceFile, targetFile " + ControlChars.Lf)
Console.WriteLine(ControlChars.Tab + " mode: e element centric" + ControlChars.Lf)
Console.WriteLine(ControlChars.Tab + " mode: a attribute centric" + ControlChars.Lf)
End Sub 'Usage
Public Sub ConertToAttributeCentric(sourceFile As FileStream, targetFile As FileStream)
' Stack is used to track how many.
Dim stack As New Stack()
Dim reader As New XmlTextReader(sourceFile)
reader.Read()
Dim writer As New XmlTextWriter(targetFile, reader.Encoding)
writer.Formatting = Formatting.Indented
Do
Select Case reader.NodeType
Case XmlNodeType.XmlDeclaration
writer.WriteStartDocument((Nothing = reader.GetAttribute("standalone") Or "yes" = reader.GetAttribute("standalone")))
Case XmlNodeType.Element
Dim element As New ElementNode(reader.Prefix, reader.LocalName, reader.NamespaceURI)
If 0 = stack.Count Then
writer.WriteStartElement(element.prefix, element.name, element.nameSpace)
element.startElement = True
End If
stack.Push(element)
Case XmlNodeType.Attribute
Throw New Exception("We should never been here!")
Case XmlNodeType.Text
Dim attribute As New ElementNode()
attribute = CType(stack.Pop(), ElementNode)
element = CType(stack.Peek(), ElementNode)
If Not element.startElement Then
writer.WriteStartElement(element.prefix, element.name, element.nameSpace)
element.startElement = True
End If
writer.WriteStartAttribute(attribute.prefix, attribute.name, attribute.nameSpace)
writer.WriteRaw(reader.Value)
reader.Read() 'jump over the EndElement
Case XmlNodeType.EndElement
writer.WriteEndElement()
stack.Pop()
Case XmlNodeType.CDATA
writer.WriteCData(reader.Value)
Case XmlNodeType.Comment
writer.WriteComment(reader.Value)
Case XmlNodeType.ProcessingInstruction
writer.WriteProcessingInstruction(reader.Name, reader.Value)
Case XmlNodeType.EntityReference
writer.WriteEntityRef(reader.Name)
Case XmlNodeType.Whitespace
writer.WriteWhitespace(reader.Value);
Case XmlNodeType.None
writer.WriteRaw(reader.Value)
Case XmlNodeType.SignificantWhitespace
writer.WriteWhitespace(reader.Value)
Case XmlNodeType.DocumentType
writer.WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value)
Case XmlNodeType.EndEntity
Case Else
Console.WriteLine(("UNKNOWN Node Type = " + CInt(reader.NodeType)))
End Select
Loop While reader.Read()
writer.WriteEndDocument()
reader.Close()
writer.Flush()
writer.Close()
End Sub 'ConertToAttributeCentric
' Use the WriteNode to simplify the process.
Public Sub ConertToElementCentric(sourceFile As FileStream, targetFile As FileStream)
Dim reader As New XmlTextReader(sourceFile)
reader.Read()
Dim writer As New XmlTextWriter(targetFile, reader.Encoding)
writer.Formatting = Formatting.Indented
Do
Select Case reader.NodeType
Case XmlNodeType.Element
writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI)
If reader.MoveToFirstAttribute() Then
Do
writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI)
writer.WriteRaw(reader.Value)
writer.WriteEndElement()
Loop While reader.MoveToNextAttribute()
writer.WriteEndElement()
End If
Case XmlNodeType.Attribute
Throw New Exception("We should never been here!")
Case XmlNodeType.Whitespace
writer.WriteWhitespace(reader.Value)
Case XmlNodeType.EndElement
writer.WriteEndElement()
Case XmlNodeType.Text
Throw New Exception("The input document is not a attribute centric document" + ControlChars.Lf)
Case Else
Console.WriteLine(reader.NodeType)
writer.WriteNode(reader, False)
End Select
Loop While reader.Read()
reader.Close()
writer.Flush()
writer.Close()
End Sub 'ConertToElementCentric
End Class 'ModeConverter
// The program will convert an element-centric document to an
// attribute-centric document or element-centric to attribute-centric.
using System;
using System.Xml;
using System.IO;
using System.Text;
using System.Collections;
class ModeConverter {
private const int bufferSize=2048;
internal class ElementNode {
String _name;
String _prefix;
String _namespace;
bool _startElement;
internal ElementNode() {
this._name = null;
this._prefix = null;
this._namespace = null;
this._startElement = false;
}
internal ElementNode(String prefix, String name, String nameSpace) {
this._name = name;
this._prefix = prefix;
this._namespace = nameSpace;
}
public String name{
get { return _name; }
}
public String prefix{
get { return _prefix; }
}
public String nameSpace{
get { return _namespace; }
}
public bool startElement{
get { return _startElement; }
set { _startElement = value;}
}
}
public static void Main(String[] args) {
ModeConverter modeConverter = new ModeConverter();
if (args[0]== null || args[0]== "?" || args.Length < 2 ) {
modeConverter.Usage();
return;
}
FileStream sourceFile = new FileStream(args[1], FileMode.Open, FileAccess.Read, FileShare.Read);
FileStream targetFile = new FileStream(args[2], FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
if (args[0] == "-a") {
modeConverter.ConertToAttributeCentric(sourceFile, targetFile);
} else {
modeConverter.ConertToElementCentric(sourceFile, targetFile);
}
return;
}
public void Usage() {
Console.WriteLine("? This help message \n");
Console.WriteLine("Convert -mode sourceFile, targetFile \n");
Console.WriteLine("\t mode: e element centric\n");
Console.WriteLine("\t mode: a attribute centric\n");
}
public void ConertToAttributeCentric(FileStream sourceFile, FileStream targetFile) {
// Stack is used to track how many.
Stack stack = new Stack();
XmlTextReader reader = new XmlTextReader(sourceFile);
reader.Read();
XmlTextWriter writer = new XmlTextWriter(targetFile, reader.Encoding);
writer.Formatting = Formatting.Indented;
do {
switch (reader.NodeType) {
case XmlNodeType.XmlDeclaration:
writer.WriteStartDocument(null == reader.GetAttribute("standalone") || "yes" == reader.GetAttribute("standalone"));
break;
case XmlNodeType.Element:
ElementNode element = new ElementNode(reader.Prefix, reader.LocalName, reader.NamespaceURI);
if (0 == stack.Count) {
writer.WriteStartElement(element.prefix, element.name, element.nameSpace);
element.startElement=true;
}
stack.Push(element);
break;
case XmlNodeType.Attribute:
throw new Exception("We should never been here!");
case XmlNodeType.Text:
ElementNode attribute = new ElementNode();
attribute = (ElementNode)stack.Pop();
element = (ElementNode)stack.Peek();
if (!element.startElement) {
writer.WriteStartElement(element.prefix, element.name, element.nameSpace);
element.startElement=true;
}
writer.WriteStartAttribute(attribute.prefix, attribute.name, attribute.nameSpace);
writer.WriteRaw(reader.Value);
reader.Read(); //jump over the EndElement
break;
case XmlNodeType.EndElement:
writer.WriteEndElement();
stack.Pop();
break;
case XmlNodeType.CDATA:
writer.WriteCData(reader.Value);
break;
case XmlNodeType.Comment:
writer.WriteComment(reader.Value);
break;
case XmlNodeType.ProcessingInstruction:
writer.WriteProcessingInstruction(reader.Name, reader.Value);
break;
case XmlNodeType.EntityReference:
writer.WriteEntityRef( reader.Name);
break;
case XmlNodeType.Whitespace:
writer.WriteWhitespace(reader.Value);
break;
case XmlNodeType.None:
writer.WriteRaw(reader.Value);
break;
case XmlNodeType.SignificantWhitespace:
writer.WriteWhitespace(reader.Value);
break;
case XmlNodeType.DocumentType:
writer.WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value);
break;
case XmlNodeType.EndEntity:
break;
default:
Console.WriteLine("UNKNOWN Node Type = " + ((int)reader.NodeType));
break;
}
} while (reader.Read());
writer.WriteEndDocument();
reader.Close();
writer.Flush();
writer.Close();
}
// Use the WriteNode to simplify the process.
public void ConertToElementCentric(FileStream sourceFile, FileStream targetFile) {
XmlTextReader reader = new XmlTextReader(sourceFile);
reader.Read();
XmlTextWriter writer = new XmlTextWriter(targetFile, reader.Encoding);
writer.Formatting = Formatting.Indented;
do {
switch (reader.NodeType) {
case XmlNodeType.Element:
writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
if (reader.MoveToFirstAttribute()) {
do {
writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
writer.WriteRaw(reader.Value);
writer.WriteEndElement();
} while(reader.MoveToNextAttribute());
writer.WriteEndElement();
}
break;
case XmlNodeType.Attribute:
throw new Exception("We should never been here!");
case XmlNodeType.Whitespace:
writer.WriteWhitespace(reader.Value);
break;
case XmlNodeType.EndElement:
writer.WriteEndElement();
break;
case XmlNodeType.Text:
throw new Exception("The input document is not a attribute centric document\n");
default:
Console.WriteLine(reader.NodeType);
writer.WriteNode(reader, false);
break;
}
} while (reader.Read());
reader.Close();
writer.Flush();
writer.Close();
}
}
Führen Sie den Code nach dem Kompilieren aus, indem Sie in der Befehlszeile <kompilierter Name> -a centric.xml <Name der Ausgabedatei> eingeben. Die Ausgabedatei muss vorhanden sein und kann eine leere Textdatei sein.
Bei der folgenden Ausgabe wird davon ausgegangen, dass das C#-Programm in centric_cs kompiliert wurde. Die Befehlszeile lautet C:\centric_cs -a centric.xml centric_out.xml.
Der Modus -a weist die Anwendung an, das Eingabe-XML in attributbasiert zu konvertieren, wohingegen der Modus -e in elementbasiert konvertiert. Die nachstehende Ausgabe ist die neue attributbasierte Ausgabe, die mit dem Modus -a generiert wurde. Die Elemente enthalten nun anstelle der geschachtelten Elemente Attribute.
Output: centric_out.xml
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<root>
<Customer firstname="Jerry" lastname="Larson">
<Order OrderID="Ord-12345">
<OrderDetail Quantity="1301" UnitPrice="$3000" ProductName="Computer" />
</Order>
</Customer>
</root>
Siehe auch
Referenz
Konzepte
Erstellen von wohlgeformtem XML mit "XmlTextWriter"