Conversion des caractères spéciaux lors de l'écriture d'un contenu XML
XmlWriter comprend une méthode, WriteRaw, qui vous permet d'écrire un balisage brut manuellement. Cette méthode empêche le remplacement sous la forme de séquences d'échappement des caractères spéciaux. Cela s'oppose à la méthode WriteString qui remplace des chaînes présentées sous la forme de séquences d'échappement par leur référence d'entité équivalente. Les caractères se présentant sous la forme de séquences d'échappement sont répertoriés dans la recommandation sur le langage XML 1.0, à la section 2.4 concernant les données de type caractère et le balisage, ainsi qu'à la section 3.3.3 concernant la normalisation de la valeur d'attribut de la recommandation sur le langage XML 1.0 (cinquième édition). Si la méthode WriteString est appelée lors de l'écriture d'une valeur d'attribut, elle la remplace par une séquence d'échappement ' et ". Les valeurs des caractères 0x-0x1F sont encodées comme entités de caractères numériques de � à , à l'exception des espaces blancs 0x9, 0x10 et 0x13.
Le principe à suivre pour utiliser WriteString ou WritingRaw est donc d'employer WriteString pour parcourir tous les caractères afin de rechercher des caractères d'entités, et WriteRaw afin d'écrire exactement ce qui est indiqué.
La méthode WriteNode copie chaque élément du nœud en cours et le lecteur est positionné à l'emplacement du writer. Le lecteur est ensuite avancé vers le nœud frère suivant pour la suite du traitement. La méthode writeNode est une solution rapide pour extraire des informations d'un document vers un autre.
Le tableau suivant illustre les types de nœuds pris en charge pour la méthode WriteNode.
Type de nœud |
Description |
---|---|
Element |
Écrit le nœud d'élément ainsi que tous les nœuds d'attribut. |
Attribute |
Pas d'opération. Utilisez WriteStartAttribute ou WriteAttributeString pour écrire l'attribut. |
Text |
Écrit le nœud de texte. |
CDATA |
Écrit le nœud de section CDATA. |
EntityReference |
Écrit le nœud Entity Ref. |
ProcessingInstruction |
Écrit le nœud PI. |
Comment |
Écrit le nœud Comment. |
DocumentType |
Écrit le nœud DocType. |
Whitespace |
Écrit le nœud Whitespace. |
SignificantWhitespace |
Écrit le nœud Whitespace. |
EndElement |
Pas d'opération. |
EndEntity |
Pas d'opération. |
L'exemple suivant illustre la différence entre les méthodes WriteString et WriteRaw lorsque le caractère « < » est donné. Cet exemple de code utilise WriteString.
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);
Sortie
<myRoot><</myRoot>
Cet exemple de code utilise WriteRaw et la sortie comprend un caractère non conforme comme contenu de l'élément.
w.WriteStartElement("myRoot")
w.WriteRaw("<")
w.WriteEndElement()
w.WriteStartElement("myRoot");
w.WriteRaw("<");
w.WriteEndElement();
Sortie
<myRoot><</myRoot>
L'exemple suivant montre comment convertir un document XML centré sur des éléments en document centré sur des attributs. Vous pouvez également reconvertir un document XML centré sur des attributs en document centré sur des éléments. Le mode centré sur des éléments signifie que le document XML a été conçu pour contenir beaucoup d'éléments mais peu d'attributs. Un design centré sur les attributs comprend moins d'éléments, et ce qui aurait été des éléments dans un design centré sur les éléments a été transformé en attributs d'éléments. Il y a donc moins d'éléments mais plus d'attributs par élément.
Cet exemple est utile, que vous ayez conçu les données XML dans l'un ou l'autre des deux modes, puisqu'elles pourront être converties dans l'autre mode.
Le code XML suivant utilise un document centré sur les éléments. Ces éléments ne contiennent pas d'attributs.
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>
L'exemple d'application suivant effectue la conversion.
' 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();
}
}
Après compilation du code, exécutez-le depuis la ligne de commande en tapant <compiled name> -a centric.xml <output file name>. Le fichier de sortie doit être présent et peut correspondre à un fichier texte vide.
Pour la sortie suivante, en supposant que le programme C# a été compilé en centric_cs, la ligne de commande est C:\centric_cs -a centric.xml centric_out.xml.
Le mode de –a demande à l'application de convertir l'entrée XML en sortie centrée sur les attributs, tandis qu'un mode de -e change cette sortie en sortie centrée sur les éléments. La sortie suivante correspond à la nouvelle sortie centrée sur les attributs générée à l'aide du mode -a. Les éléments contiennent désormais des attributs au lieu d'éléments imbriqués.
Sortie : 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>
Voir aussi
Référence
Concepts
Création de XML correctement construit avec XmlTextWriter