Quand je construis XML à partir de zéro avec XmlDocument
, la propriété OuterXml
a déjà tout mis en retrait avec des sauts de ligne. Cependant, si j'appelle LoadXml
sur un XML très "compressé" (sans saut de ligne ni indentation), la sortie de OuterXml
reste ainsi. Alors ...
Quel est le moyen le plus simple d’obtenir une sortie XML embellie à partir d’une instance de XmlDocument
?
Sur la base des autres réponses, j’ai étudié XmlTextWriter
et proposé la méthode d’aide suivante:
static public string Beautify(this XmlDocument doc)
{
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
NewLineChars = "\r\n",
NewLineHandling = NewLineHandling.Replace
};
using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
doc.Save(writer);
}
return sb.ToString();
}
C'est un peu plus de code que je ne l'aurais espéré, mais cela fonctionne très bien.
Selon l'adaptation de Erika Ehrli's blog, cela devrait le faire:
XmlDocument doc = new XmlDocument();
doc.LoadXml("<item><name>wrench</name></item>");
// Save the document to a file and auto-indent the output.
using (XmlTextWriter writer = new XmlTextWriter("data.xml", null)) {
writer.Formatting = Formatting.Indented;
doc.Save(writer);
}
Ou encore plus facile si vous avez accès à Linq
try
{
RequestPane.Text = System.Xml.Linq.XElement.Parse(RequestPane.Text).ToString();
}
catch (System.Xml.XmlException xex)
{
displayException("Problem with formating text in Request Pane: ", xex);
}
Une version de méthode d'extension plus courte
public static string ToIndentedString( this XmlDocument doc )
{
var stringWriter = new StringWriter(new StringBuilder());
var xmlTextWriter = new XmlTextWriter(stringWriter) {Formatting = Formatting.Indented};
doc.Save( xmlTextWriter );
return stringWriter.ToString();
}
Si la méthode Beautify ci-dessus est appelée pour un XmlDocument
qui contient déjà un nœud enfant XmlProcessingInstruction
, l'exception suivante est levée:
Impossible d'écrire une déclaration XML. La méthode WriteStartDocument l'a déjà écrite.
Voici ma version modifiée de la version originale pour se débarrasser de l'exception:
private static string beautify(
XmlDocument doc)
{
var sb = new StringBuilder();
var settings =
new XmlWriterSettings
{
Indent = true,
IndentChars = @" ",
NewLineChars = Environment.NewLine,
NewLineHandling = NewLineHandling.Replace,
};
using (var writer = XmlWriter.Create(sb, settings))
{
if (doc.ChildNodes[0] is XmlProcessingInstruction)
{
doc.RemoveChild(doc.ChildNodes[0]);
}
doc.Save(writer);
return sb.ToString();
}
}
Cela fonctionne pour moi maintenant, probablement vous auriez besoin d'analyser tous les nœuds enfants pour le nœud XmlProcessingInstruction
, pas seulement le premier?
Mise à jour avril 2015:
Comme j'avais un autre cas où l'encodage était incorrect, j'ai cherché comment appliquer UTF-8 sans nomenclature. J'ai trouvé cet article de blog et créé une fonction basée sur celle-ci:
private static string beautify(string xml)
{
var doc = new XmlDocument();
doc.LoadXml(xml);
var settings = new XmlWriterSettings
{
Indent = true,
IndentChars = "\t",
NewLineChars = Environment.NewLine,
NewLineHandling = NewLineHandling.Replace,
Encoding = new UTF8Encoding(false)
};
using (var ms = new MemoryStream())
using (var writer = XmlWriter.Create(ms, settings))
{
doc.Save(writer);
var xmlString = Encoding.UTF8.GetString(ms.ToArray());
return xmlString;
}
}
XmlTextWriter xw = new XmlTextWriter(writer);
xw.Formatting = Formatting.Indented;
public static string FormatXml(string xml)
{
try
{
var doc = XDocument.Parse(xml);
return doc.ToString();
}
catch (Exception)
{
return xml;
}
}
Un moyen simple consiste à utiliser:
writer.WriteRaw(space_char);
Comme cet exemple de code, ce code correspond à ce que j’avais utilisé pour créer une vue arborescente semblable à une structure utilisant XMLWriter:
private void generateXML(string filename)
{
using (XmlWriter writer = XmlWriter.Create(filename))
{
writer.WriteStartDocument();
//new line
writer.WriteRaw("\n");
writer.WriteStartElement("treeitems");
//new line
writer.WriteRaw("\n");
foreach (RootItem root in roots)
{
//indent
writer.WriteRaw("\t");
writer.WriteStartElement("treeitem");
writer.WriteAttributeString("name", root.name);
writer.WriteAttributeString("uri", root.uri);
writer.WriteAttributeString("fontsize", root.fontsize);
writer.WriteAttributeString("icon", root.icon);
if (root.children.Count != 0)
{
foreach (ChildItem child in children)
{
//indent
writer.WriteRaw("\t");
writer.WriteStartElement("treeitem");
writer.WriteAttributeString("name", child.name);
writer.WriteAttributeString("uri", child.uri);
writer.WriteAttributeString("fontsize", child.fontsize);
writer.WriteAttributeString("icon", child.icon);
writer.WriteEndElement();
//new line
writer.WriteRaw("\n");
}
}
writer.WriteEndElement();
//new line
writer.WriteRaw("\n");
}
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
De cette façon, vous pouvez ajouter des sauts de tabulation ou de ligne comme d'habitude, c'est-à-dire\t ou\n
Une approche plus simplifiée basée sur la réponse acceptée:
static public string Beautify(this XmlDocument doc) {
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true
};
using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
doc.Save(writer);
}
return sb.ToString();
}
Définir la nouvelle ligne n'est pas nécessaire. Les caractères d'indentation ont également les deux espaces par défaut, j'ai donc préféré ne pas le définir également.
Si vous avez une chaîne de code XML plutôt qu'un document prêt à être utilisé, vous pouvez le faire comme suit:
var xmlString = "<xml>...</xml>"; // Your original XML string that needs indenting.
xmlString = this.PrettifyXml(xmlString);
private string PrettifyXml(string xmlString)
{
var prettyXmlString = new StringBuilder();
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlString);
var xmlSettings = new XmlWriterSettings()
{
Indent = true,
IndentChars = " ",
NewLineChars = "\r\n",
NewLineHandling = NewLineHandling.Replace
};
using (XmlWriter writer = XmlWriter.Create(prettyXmlString, xmlSettings))
{
xmlDoc.Save(writer);
}
return prettyXmlString.ToString();
}
Lors de la mise en œuvre des suggestions affichées ici, le codage du texte me posait problème. Il semble que l'encodage de XmlWriterSettings
soit ignoré et toujours remplacé par l'encodage du flux. Lorsque vous utilisez StringBuilder
, il s'agit toujours du codage de texte utilisé en interne en C #, à savoir UTF-16.
Voici donc une version qui prend également en charge d’autres encodages.
REMARQUE IMPORTANTE: le formatage est complètement ignoré si votre propriété XMLDocument
a sa propriété preserveWhitespace
activée lors du chargement du document. Cela m'a stoppé pendant un moment, alors assurez-vous de ne pas l'activer.
Mon code final:
public static void SaveFormattedXml(XmlDocument doc, String outputPath, Encoding encoding)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = "\t";
settings.NewLineChars = "\r\n";
settings.NewLineHandling = NewLineHandling.Replace;
using (MemoryStream memstream = new MemoryStream())
using (StreamWriter sr = new StreamWriter(memstream, encoding))
using (XmlWriter writer = XmlWriter.Create(sr, settings))
using (FileStream fileWriter = new FileStream(outputPath, FileMode.Create))
{
if (doc.ChildNodes.Count > 0 && doc.ChildNodes[0] is XmlProcessingInstruction)
doc.RemoveChild(doc.ChildNodes[0]);
// save xml to XmlWriter made on encoding-specified text writer
doc.Save(writer);
// Flush the streams (not sure if this is really needed for pure mem operations)
writer.Flush();
// Write the underlying stream of the XmlWriter to file.
fileWriter.Write(memstream.GetBuffer(), 0, (Int32)memstream.Length);
}
}
Cela enregistrera le XML formaté sur le disque, avec le codage de texte donné.