- はじめに 「XML整形」は“タグの森に道をつける”作業
- 基本方針 「文字列いじり」ではなく「XMLとしてパースしてから整形」
- 実務で使いやすい基本実装 XDocument を使った整形
- 重要ポイント1 XDocument.Parse で“XMLとして正しいか”をチェックする
- 重要ポイント2 XmlWriterSettings で“整形ルール”を決める
- 重要ポイント3 XmlWriter で“構造に従って書き直す”
- 動作例でイメージを固める
- エラーや不正XMLへの向き合い方
- XmlDocument を使った別パターンも知っておく
- 業務ユーティリティとしてどうまとめるか
- まとめ 「XML整形ユーティリティ」は“XMLの中身を理解するための必需品”
はじめに 「XML整形」は“タグの森に道をつける”作業
XMLは、設定ファイル、外部システム連携、古めのWebサービスなどで今でもよく使われますが、そのまま1行でベタッと出てくると、とても読みにくいです。
XML整形(Pretty Print)は、そのぐちゃっとしたXMLに改行とインデントを付けて、「人間が読める形」にする処理です。
C#では、XMLを「文字列として置換する」のではなく、「一度パースしてから、整形オプション付きで書き出す」のが正しいやり方です。
ここをきちんと押さえておくと、ログ、デバッグ、ツール作成などでかなり役に立つユーティリティになります。
基本方針 「文字列いじり」ではなく「XMLとしてパースしてから整形」
なぜ単純な文字列置換ではダメなのか
初心者がやりがちなのは、XML文字列に対して直接、改行やスペースを入れたり消したりする方法です。
例えば、「>< の間に改行を入れる」「> の後に改行を入れる」といったやり方です。
これはすぐに破綻します。
タグの中の属性値に > や < が出てくる場合、CDATAセクションの中に記号が出てくる場合、コメントや処理命令の中に特殊な文字が出てくる場合など、「XMLの構造」と「ただの文字」を区別できないからです。
正しいやり方は、XMLパーサに一度読ませて「構造」として理解させ、その構造を「インデント付きで書き出す」ことです。
C#では、System.Xml.Linq.XDocument や System.Xml.XmlDocument を使うのが定番です。
実務で使いやすい基本実装 XDocument を使った整形
一番シンプルで実務投入しやすいユーティリティ
まずは、XDocument を使った XML 整形ユーティリティの完成形を見てみます。
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
public static class XmlFormatUtil
{
public static string FormatXml(string? xml)
{
if (string.IsNullOrWhiteSpace(xml))
{
return string.Empty;
}
try
{
var doc = XDocument.Parse(xml, LoadOptions.PreserveWhitespace);
var settings = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
NewLineChars = "\n",
NewLineHandling = NewLineHandling.Replace,
OmitXmlDeclaration = false
};
using var sw = new StringWriter();
using (var xw = XmlWriter.Create(sw, settings))
{
doc.Save(xw);
}
return sw.ToString();
}
catch (XmlException)
{
return xml;
}
}
}
C#このメソッドだけで、「ぐちゃぐちゃXMLをきれいに整形する」ことができます。
ここから、重要なポイントを一つずつかみ砕いていきます。
重要ポイント1 XDocument.Parse で“XMLとして正しいか”をチェックする
XDocument.Parse(xml, LoadOptions.PreserveWhitespace); は、文字列をXMLとしてパースする処理です。
ここで XML の構造が解析され、ルート要素、子要素、属性、コメントなどがオブジェクトとして扱えるようになります。
LoadOptions.PreserveWhitespace を付けているのは、「元の空白も一応保持したい」場合に使うオプションです。
ただし、整形時には XmlWriterSettings 側のルールでインデントや改行が付け直されるので、「空白を完全にそのまま残す」わけではありません。
ここでは、「パーサにとって意味のある空白(テキストノードとしての空白)」を落とさないための保険程度に考えておけば大丈夫です。
もし XML が壊れている場合、XDocument.Parse は XmlException を投げます。
今回のユーティリティでは、キャッチして「元の文字列をそのまま返す」方針にしています。
これにより、「整形できないけれど、とりあえず中身は見たい」というログ・デバッグ用途でも使いやすくなります。
重要ポイント2 XmlWriterSettings で“整形ルール”を決める
XmlWriterSettings は、「XMLを書き出すときのルール」をまとめて指定するクラスです。
ここが XML 整形ユーティリティの“性格”を決める重要な部分です。
Indent = true は、「インデントを付ける」指定です。
これを true にしないと、1行に詰まったXMLが出力されます。
IndentChars = " " は、インデントに使う文字列です。
ここでは半角スペース2つにしていますが、タブ "\t" にしたり、スペース4つにしたり、プロジェクトのコーディングスタイルに合わせて変えられます。
NewLineChars = "\n" は、改行コードを指定しています。
Windows標準は "\r\n" ですが、ログやツールで扱いやすいように "\n" に統一することもよくあります。
ここをどうするかは、「どこで使うXMLか」によって決めるとよいです。
NewLineHandling = NewLineHandling.Replace は、「元の改行を指定した改行コードに置き換える」指定です。
これにより、元のXMLが \r\n でも \n でも、出力は常に NewLineChars に揃います。
OmitXmlDeclaration = false は、「XML宣言(<?xml version="1.0" encoding="utf-8"?>)を出力するかどうか」です。
false にしておくと、宣言が付いた状態で出力されます。
宣言を付けたくない場合は true にします。
この設定をユーティリティ側で固定しておくことで、「プロジェクト内のXML整形ルール」が統一されます。
重要ポイント3 XmlWriter で“構造に従って書き直す”
XmlWriter.Create(sw, settings) で、先ほどの設定を使った XML ライターを作っています。doc.Save(xw); で、XDocument の内容をそのライターに書き出します。
ここで大事なのは、「文字列をいじっているのではなく、構造をもとに書き直している」という点です。
タグの入れ子、属性の位置、コメント、テキストノードなど、XMLとしての構造を理解したうえで、インデントと改行を付けて出力してくれます。
結果として、元のXMLが
<root><item id="1"><name>Taro</name></item><item id="2"><name>Jiro</name></item></root>
のような1行でも、整形後は
<?xml version="1.0" encoding="utf-16"?>
<root>
<item id="1">
<name>Taro</name>
</item>
<item id="2">
<name>Jiro</name>
</item>
</root>
のように、読みやすい形になります(encoding は StringWriter の実装に依存します)。
動作例でイメージを固める
ぐちゃぐちゃXMLを整形する例
string raw = "<root><item id=\"1\"><name>Taro</name></item><item id=\"2\"><name>Jiro</name></item></root>";
string formatted = XmlFormatUtil.FormatXml(raw);
Console.WriteLine(formatted);
C#出力イメージは次のようになります。
<?xml version="1.0" encoding="utf-16"?>
<root>
<item id="1">
<name>Taro</name>
</item>
<item id="2">
<name>Jiro</name>
</item>
</root>
タグの入れ子が一目で分かるようになり、「どこに何があるか」を目で追いやすくなります。
すでに整形済みのXMLを渡した場合
すでにインデント付きのXMLを渡しても、一度パースしてから書き直すので、インデントスタイルや改行コードがユーティリティ側のルールに揃います。
「バラバラなスタイルのXMLを、プロジェクト標準のスタイルに揃える」という用途にも使えます。
エラーや不正XMLへの向き合い方
XMLは、タグの閉じ忘れ、属性の引用符抜け、ルート要素が複数ある、などの理由で簡単に壊れます。XDocument.Parse は厳密なので、こうした不正XMLに対しては XmlException を投げます。
今回のユーティリティでは、catch (XmlException) で例外を受け取り、そのまま元の文字列を返すようにしています。
これにより、「整形はできないが、とりあえずログに出して中身を見たい」というケースでも、アプリ全体が落ちることなく扱えます。
もし「不正XMLは絶対に許さない」という要件なら、ここで例外を再スローする実装に変えればOKです。
大事なのは、「ユーティリティ側で方針を決めておく」ことです。
XmlDocument を使った別パターンも知っておく
既存のコードベースでは、XmlDocument を使っていることも多いです。
同じような整形ユーティリティは、次のように書けます。
using System;
using System.IO;
using System.Text;
using System.Xml;
public static class XmlFormatUtilLegacy
{
public static string FormatXml(string? xml)
{
if (string.IsNullOrWhiteSpace(xml))
{
return string.Empty;
}
try
{
var doc = new XmlDocument();
doc.LoadXml(xml);
var settings = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
NewLineChars = "\n",
NewLineHandling = NewLineHandling.Replace,
OmitXmlDeclaration = false
};
using var sw = new StringWriter();
using (var xw = XmlWriter.Create(sw, settings))
{
doc.Save(xw);
}
return sw.ToString();
}
catch (XmlException)
{
return xml;
}
}
}
C#考え方は XDocument 版と同じで、「パースしてから XmlWriter で整形して書き出す」という流れです。
新規開発なら XDocument を使うほうが扱いやすいことが多いですが、既存コードに合わせて XmlDocument 版も書けると便利です。
業務ユーティリティとしてどうまとめるか
XML整形は、ログやデバッグで特に威力を発揮します。
例えば、次のようなラッパーを用意しておくと、呼び出し側がとても楽になります。
public static class XmlPretty
{
public static string Format(string? xml)
=> XmlFormatUtil.FormatXml(xml);
}
C#これで、ログ出力時に
logger.LogInformation(XmlPretty.Format(responseXml));
C#のように一行で「整形してから出す」が書けます。
また、null や空文字の扱いもユーティリティ側で決めておくと、呼び出し側で毎回 null チェックを書く必要がなくなります。
今回の実装では、null や空白だけの文字列は空文字を返すようにしており、「とりあえず何かしら文字列が返ってくる」前提で扱えるようにしています。
まとめ 「XML整形ユーティリティ」は“XMLの中身を理解するための必需品”
XML整形は、単なる見た目の問題ではなく、「構造を理解しやすくするための道具」です。
業務でXMLを扱うなら、ほぼ確実に欲しくなるユーティリティです。
押さえておきたいポイントは、次の通りです。
文字列置換ではなく、必ず XML としてパースしてから整形すること。XDocument.Parse + XmlWriterSettings(Indent = true) + doc.Save(XmlWriter) という流れを覚えておくこと。
インデント文字、改行コード、XML宣言の有無などを XmlWriterSettings で統一しておくと、プロジェクト全体のスタイルが揃うこと。
不正XMLへの扱い(例外にするか、元の文字列を返すか)をユーティリティ側で決めておくと、呼び出し側がシンプルになること。
ここまで理解できれば、「なんとなくXMLを眺めている」段階から一歩進んで、
“読みやすさとデバッグ効率を一気に上げるXML整形ユーティリティ”を、自分のC#コードに自然に組み込めるようになっていきます。
