XmlDocumentから改行してインデントされたXMLを取得する最も簡単な方法は何ですか?


105

を使用してXMLを最初から作成するXmlDocumentと、OuterXmlプロパティはすべて改行でインデントされています。ただし、LoadXml非常に「圧縮された」XML(改行やインデントなし)を呼び出すと、の出力OuterXmlはそのままになります。そう ...

のインスタンスから美化されたXML出力を取得する最も簡単な方法は何XmlDocumentですか?

回答:


209

他の答えに基づいて、私は調べてXmlTextWriter次のヘルパーメソッドを思いつきました:

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

それは私が望んだよりも少し多くのコードですが、それはちょうど桃色に動作します。


5
ユーティリティメソッドをXmlDocumentクラスの拡張メソッドとして作成することも検討できます。
反対側の

5
奇妙なことに、私にとっては、これはxmlヘッダーのエンコーディングをUTF-16に設定する以外は何もしません。不思議なことに、私が明示的に設定した場合でも、これは行われますsettings.Encoding = Encoding.UTF8;
Nyerguds 2013年

3
エンコーディングの問題は、の代わりに指定されたエンコーディングでMemoryStream+ StreamWriterを使用しStringBuilder、でテキストを取得することで解決できますenc.GetString(memstream.GetBuffer(), 0, (int)memstream.Length);。ただし、最終結果はまだフォーマットされていません。既に書式設定されている既読ドキュメントから始めていることは関係がありますか?新しいノードも同様にフォーマットする必要があります。
Nyerguds 2013年

2
をに変更したく"\r\n"なりEnvironment.Newlineます。
Pharap、2015

2
doc.PreserveWhitespacetrueに設定しないでください。それ以外の場合、すでに部分的なインデントが含まれていると失敗します。
マスターDJon

48

適応したようエリカEhrliさんのブログ、これはそれを行う必要があります。

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

10
usingステートメントを閉じると、Dispose()呼び出し時にライターが自動的に閉じます。
タイラーリー

3
私にとっては、これは1行だけインデントします。インデントされていない他の数十行がまだあります。
Cジョンソン

40

または、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);
}

非常に素晴らしい!親指受け入れ答えを超える利点は、それがそうXMLフラグメントのために良い作品XMLコメントを生成しないということです
ウマルファルークそのKhawaja

3
奇妙なことに、これによりXMLから<?xml ...?>およびが削除さ<!DOCTYPE ...>れます。フラグメントには問題ありませんが、完全なドキュメントには望ましくありません。
Jesse Chisholm

これは私のために働いた唯一の方法です。xmltextwriter、Formatting = Formatting.Indented、およびXmlWriterSettingsを使用する他のすべてのメソッドはテキストを再フォーマットしませんが、このメソッドはします。
kexx

16

短い拡張メソッドバージョン

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

これは非常にうまく機能し、ディスクへの不要なファイルを作成する必要としない
ザインRizvi

13

上記のBeautifyメソッドがXmlDocumentすでにXmlProcessingInstruction子ノードを含むに対して呼び出されている場合、次の例外がスローされます。

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

cdataセクションを親ノード内で子ノードの前に配置すると機能しません
Sasha Bond

2
MemoryStreamは、少なくとも私の側では必要ないようです。設定で私が設定:Encoding = Encoding.UTF8OmitXmlDeclaration = true
マスターDJonに

7
XmlTextWriter xw = new XmlTextWriter(writer);
xw.Formatting = Formatting.Indented;

5
    public static string FormatXml(string xml)
    {
        try
        {
            var doc = XDocument.Parse(xml);
            return doc.ToString();
        }
        catch (Exception)
        {
            return xml;
        }
    }

以下の答えは間違いなくいくつかの説明でうまくいきますが、それは私にとってはうまくいき、他の解決策よりもはるかに簡単です。
CarlR、2015年

PS 3でこれを機能させるには、system.link.XMLアセンブリをインポートする必要があるようです
CarlR

2

簡単な方法は以下を使用することです:

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


1

ここに投稿された提案を実装するとき、テキストのエンコードに問題がありました。のエンコーディングXmlWriterSettingsは無視されているようで、常にストリームのエンコーディングによってオーバーライドされます。を使用する場合StringBuilder、これは常にC#で内部的に使用されるテキストエンコーディング、つまりUTF-16です。

これが他のエンコーディングもサポートするバージョンです。

重要な注意:ドキュメントの読み込み時にXMLDocumentオブジェクトのpreserveWhitespaceプロパティが有効になっている場合、フォーマットは完全に無視されます。これでしばらく困惑したので、有効にしないでください。

私の最後のコード:

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が、指定されたテキストエンコーディングでディスクに保存されます。


1

使用可能なドキュメントではなく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();
}

1

受け入れられた回答に基づく、より単純化されたアプローチ:

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

新しい行を設定する必要はありません。インデント文字にもデフォルトの2つのスペースがあるので、私もそれを設定しないことを選択しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.