C# 从XmlDocument获取带换行符的缩进XML的最简单方法是什么?

C# 从XmlDocument获取带换行符的缩进XML的最简单方法是什么?,c#,.net,xmldocument,outerxml,C#,.net,Xmldocument,Outerxml,当我用XmlDocument从头开始构建XML时,OuterXml属性已经用换行符将所有内容很好地缩进。但是,如果我对一些非常“压缩”的XML(没有换行或缩进)调用LoadXml,则OuterXml的输出将保持这种状态。所以 从XmlDocument的实例中获得美化的XML输出的最简单方法是什么?根据blog改编,应该这样做: XmlDocument doc = new XmlDocument(); doc.LoadXml("<item><name>wrench</

当我用
XmlDocument
从头开始构建XML时,
OuterXml
属性已经用换行符将所有内容很好地缩进。但是,如果我对一些非常“压缩”的XML(没有换行或缩进)调用
LoadXml
,则
OuterXml
的输出将保持这种状态。所以

XmlDocument
的实例中获得美化的XML输出的最简单方法是什么?

根据blog改编,应该这样做:

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);
}
XmlDocument doc=新的XmlDocument();
doc.LoadXml(“扳手”);
//将文档保存到文件并自动缩进输出。
使用(XmlTextWriter=newxmltextwriter(“data.xml”,null)){
writer.Formatting=格式化.缩进;
保存文档(编写器);
}

根据其他答案,我研究并提出了以下帮助方法:

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();
}

它的代码比我希望的要多一些,但效果很好。

如果对已包含
XmlProcessingInstruction
子节点的
XmlDocument
调用上述美化方法,则会引发以下异常:

无法写入XML声明。 WriteStartDocument方法已经存在 写的

这是我对原始版本的修改版本,以消除异常:

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();
    }
}
它现在对我有效,可能您需要扫描所有子节点以查找
XmlProcessingInstruction
节点,而不仅仅是第一个


2015年4月更新:

因为我有另一个编码错误的例子,所以我搜索了如何在没有BOM的情况下实施UTF-8。我发现并基于它创建了一个函数:

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;
    }
}

较短的扩展方法版本

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();
}

一个简单的方法是使用:

writer.WriteRaw(space_char);
与此示例代码类似,此代码是我用来使用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();

            }

        }

通过这种方式,您可以按通常使用的方式添加制表符或换行符,即。\t或\n

,如果您可以访问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);
}

在实现此处发布的建议时,我遇到了文本编码方面的问题。似乎
XmlWriterSettings
的编码被忽略,并且总是被流的编码覆盖。使用
StringBuilder
时,这始终是C#内部使用的文本编码,即UTF-16

这是一个支持其他编码的版本

重要提示:如果加载文档时,
XMLDocument
对象启用了其
preservewitspace
属性,则格式设置将完全忽略。这让我困惑了一段时间,所以请确保不要启用它

我的最终代码:

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);
    }
}

这将使用给定的文本编码将格式化的xml保存到磁盘。

如果您有一个xml字符串,而不是一个可供使用的文档,则可以通过以下方式执行:

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();
}
var xmlString=“…”;//需要缩进的原始XML字符串。
xmlString=this.PrettifyXml(xmlString);
私有字符串PrettifyXml(字符串xmlString)
{
var prettyXmlString=新的StringBuilder();
var xmlDoc=新的XmlDocument();
LoadXml(xmlString);
var xmlSettings=new XmlWriterSettings()
{
缩进=真,
缩进字符=”,
NewLineChars=“\r\n”,
NewLineHandling=NewLineHandling.Replace
};
使用(XmlWriter=XmlWriter.Create(prettyXmlString,xmlSettings))
{
保存(writer);
}
返回prettyXmlString.ToString();
}

基于公认答案的更简化方法:

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(); 
}

不需要设置新行。缩进字符也有默认的两个空格,所以我最好不要设置它。

您甚至可以考虑将实用工具方法作为XMLDoice类的扩展方法来创建。奇怪的是,对我来说,除了将XML标题的编码设置为UTF 16之外,什么也不做。奇怪的是,即使我显式地设置
settings.Encoding=Encoding.UTF8
编码问题可以通过使用具有指定编码的
MemoryStream
+
StreamWriter
来解决,而不是使用
StringBuilder
,并使用
enc.GetString(memstream.GetBuffer(),0,(int)memstream.Length)获取文本。不过,最终结果仍然没有格式化。这可能与我从一个已经有格式的已读文档开始有关吗?我只希望我的新节点也被格式化。我试图将
“\r\n”
修改为
环境。Newline
doc.PreserveWhitespace
不应设置为true。否则,如果它已经包含部分缩进,它将失败。这非常有效,并且不需要在磁盘上创建不必要的文件。非常好!与公认的答案相比,竖起大拇指的优势在于它不会生成XML注释,因此对于XML片段效果更好。奇怪的是,这会从XML中删除
。对于一个片段来说是可以的,但是对于一个完整的文档来说是不可取的。这是唯一适合我的方法。使用xmltextwriter的所有其他方法,Formatting=Formatting.Indented和XmlWriterSettings不会重新格式化文本,但是这个方法确实如此。下面的答案肯定需要一些解释,但是它对我有效,并且比其他解决方案简单得多。似乎您需要导入system.link.XML程序集才能在PS 3上工作。使用
语句关闭
将在
Dispose()时自动关闭编写器
被调用。对我来说,这只缩进一行。我还有几十行没有缩进。如果您将cdata节放在父节点内,子节点前,它将不起作用。至少在我这方面,似乎不需要emoryStream。在设置中,我设置:
Encoding=Encoding.UTF8
ommitXMLDeclaration=true
保留空白会破坏XmlWriter的格式化功能这一事实是一个关键的信息
using System;
using System.Xml;
using System.Xml.Linq;

class Program
{
    static void Main()
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\Customers.xml");

        /* another function of XDocument.Parse() is formatting XML code which gived as string: */
        Console.WriteLine(XDocument.Parse(xmlDoc.InnerXml));
    }
}
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(); 
}
using System;
using System.Xml;
using System.Xml.Linq;

class Program
{
    static void Main()
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\Customers.xml");

        /* another function of XDocument.Parse() is formatting XML code which gived as string: */
        Console.WriteLine(XDocument.Parse(xmlDoc.InnerXml));
    }
}