System.Xml.XmlWriterSettingsクラスを用いると、XmlWriterクラスでのXML文書の出力時に使用する文字コード、インデントや改行などのフォーマットを細かく指定することができます。 また、テキストノード内の改行文字をエンティティ化して改行文字の種類を維持するかどうか、XML宣言を出力するかどうかといった出力時の動作も指定することができます。
デフォルト設定のXmlWriterでは、UTF-8を用いてXMLを出力する際にBOM(Byte Order Mark)を付与しますが、XmlWriterSettingsでBOMを出力しないEncodingを設定することによってXmlWriterにBOMなしのXMLを出力させるようにすることができます。
XmlWriterとXmlWriterSettings
XDocument.SaveメソッドやXmlDocument.Saveメソッドでは、直接ファイル名を指定して保存するほかにも、XmlWriterを用いて保存することができるようになっています。
XmlWriterSettingsで出力時のオプションを設定し、その設定に従って動作するXmlWriterをSaveメソッドに指定することで、出力時のフォーマットや動作を制御することができます。
using System;
using System.Text;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
// 出力したいXMLツリー
var doc = new XDocument(
new XElement("root",
new XElement("foo",
new XElement("bar", "baz")
),
new XElement("ほげ", "もげ")
)
);
// XmlWriterの設定
var settings = new XmlWriterSettings();
// 文字コードにShift_JISを用いる
settings.Encoding = Encoding.GetEncoding("Shift_JIS");
// インデントにタブ1文字を用いる
settings.Indent = true;
settings.IndentChars = "\t";
// ファイル"output.xml"に出力するXmlWriterを作成する
using (var writer = XmlWriter.Create("output.xml", settings)) {
// 作成したXmlWriterを使ってXMLツリーを出力する
doc.Save(writer);
}
}
}
<?xml version="1.0" encoding="shift_jis"?>
<root>
<foo>
<bar>baz</bar>
</foo>
<ほげ>もげ</ほげ>
</root>
XmlWriterの作成・出力先の指定
XmlWriterSettingsからXmlWriterを作成するにはXmlWriter.Createメソッドを使います。 第一引数には出力先となるファイル名を指定することができるほか、Stream派生クラスやStreamWriterなどのTextWriter派生クラスを出力先として指定することもできます。 第二引数には出力時のオプションが設定されたXmlWriterSettingsを指定します。
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
// 出力したいXMLツリー
var doc = new XDocument(
new XElement("root",
new XElement("foo",
new XElement("bar", "baz")
),
new XElement("ほげ", "もげ")
)
);
// 出力時の設定
var settings = new XmlWriterSettings();
/* オプションの設定は省略 */
// ファイル"test.xml"に出力するXmlWriterを作成する
using (var writer = XmlWriter.Create("test.xml", settings)) {
doc.Save(writer); // 上記のXmlWriterを使って出力
}
using (var stream = new MemoryStream()) {
// MemoryStreamに出力するXmlWriterを作成する
using (var writer = XmlWriter.Create(stream, settings)) {
doc.Save(writer); // 上記のXmlWriterを使って出力
}
}
}
}
このようにMemoryStreamを用いればXMLツリーをメモリ上に展開することもできます。
XmlWriterを使ったXMLツリーの文字列化
XmlWriter.CreateメソッドでStringWriterを出力先に指定すれば、出力結果を文字列として取得することができます。 StringWriterを出力先とすることで、XMLツリーを文字列化する際にXmlWriterSettingsによって出力時のフォーマットを細かく指定することができます。
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
// 出力したいXMLツリー
var doc = new XDocument(
new XElement("root",
new XElement("foo",
new XElement("bar", "baz")
),
new XElement("ほげ", "もげ")
)
);
// 出力先となるStringWriter
using (var sw = new StringWriter()) {
// 出力時の設定
var settings = new XmlWriterSettings() {
Indent = true, // インデントする
CloseOutput = false, // XmlWriterを閉じる際にベースとなるStringWriterを閉じない
};
// StringWriterに出力するXmlWriterを作成する
using (var writer = XmlWriter.Create(sw, settings)) {
// XmlWriterを使ってXMLツリーを出力する
doc.Save(writer);
}
// 出力された内容を表示する
Console.WriteLine(sw.ToString());
}
}
}
<?xml version="1.0" encoding="utf-16"?>
<root>
<foo>
<bar>baz</bar>
</foo>
<ほげ>もげ</ほげ>
</root>
この例ではXmlWriterSettings.CloseOutputにfalseを指定しています。 これにより、XmlWriterを閉じる際に出力先となるStringWriterを閉じないようになります。 このようにすることで、XmlWriterを使い終わったあともStringWriterを使い続けることができるようにしています。
逆にXmlWriterSettings.CloseOutputにtrueを指定すると、XmlWriterが閉じられる際に元になったStream/TextWriterも同時に閉じるようになります。
XmlWriterSettingsで設定できる動作
ここまでで触れてきたような内容の他にも、XmlWriterSettingsでは以下のような動作を設定することができます。
| XmlWriterSettingsのプロパティ | 動作 | 解説 | |
|---|---|---|---|
| 出力フォーマットに関するプロパティ | XmlWriterSettings.Encoding | XmlWriterでの出力時に用いるエンコーディング(System.Text.Encoding)を指定する。 特に指定しなかった場合はデフォルトでUTF-8(BOM付き)が用いられる。 (どちらの場合もXDeclarationやXmlDeclarationによるエンコーディングの指定が上書きされる) |
§.エンコーディング (XmlWriterSettings.Encoding) |
| XmlWriterSettings.Indent |
trueを指定した場合、要素のインデントと要素間の改行を行う。インデント文字にはXmlWriterSettings.IndentCharsで指定された文字列を用いる。 改行文字にはXmlWriterSettings.NewLineCharsで指定された文字列を用いる。 falseを指定した場合、インデントと改行は行われない。(デフォルトは false: インデントしない) |
§.インデント (XmlWriterSettings.Indent, IndentChars) | |
| XmlWriterSettings.IndentChars | Indentがtrueの場合に要素のインデントに用いる空白文字を指定する。(デフォルトは " ": U+0020 'SPACE' 半角空白が2つ) |
||
| XmlWriterSettings.NewLineChars | Indentがtrueの場合に要素間の改行に用いる改行文字を指定する。NewLineOnAttributesが trueの場合は、こプロパティに指定した文字列が属性間の改行にも用いられる。(デフォルトは "\n": U+000A 'LINE FEED' ラインフィードが1つ) |
§.要素間・属性間の改行 (XmlWriterSettings.NewLineChars, NewLineOnAttributes) | |
| XmlWriterSettings.NewLineOnAttributes |
trueを指定した場合(かつIndentもtrueの場合)、属性間の改行を行い、各属性が一行ごとに記述される。falseを指定した場合、属性は要素と同じ行で一行に記述される。(デフォルトは false: 属性間の改行を行わない) |
||
| XmlWriterSettings.NewLineHandling |
NewLineHandling.Entitizeを指定した場合、テキストノードの"\r"(U+000D 'CARRIAGE RETURN')がエンティティ化(="
")された上で出力される。NewLineHandling.Replaceを指定した場合、テキストノードの"\r"および"\n"(U+000A 'LINE FEED')がNewLineCharsで設定されている改行文字に統一された上で出力される。この指定は属性値における改行文字・タブ文字にも影響する。 (デフォルトはNewLineHandling.None: そのままにする) |
§.テキストノード・属性値の改行の処理 (XmlWriterSettings.NewLineHandling) | |
| XmlWriterSettings.OmitXmlDeclaration |
trueを指定した場合、XML宣言の出力を省略する。falseを指定した場合、XML宣言の出力を省略しない。(デフォルトは false: XML宣言を出力する) |
§.XML宣言の省略 (XmlWriterSetings.OmitXmlDeclaration) | |
| XmlWriterSettings.NamespaceHandling |
NamespaceHandling.OmitDuplicatesを指定した場合、XMLツリー内に重複する名前空間宣言が存在する場合はそれを省略して出力する。 NamespaceHandling.Defaultを指定した場合、重複する名前空間宣言が存在していてもそのまま出力する。 (デフォルトは NamespaceHandling.Default: 重複する名前空間宣言を省略しない) |
§.重複する名前空間宣言の処理 (XmlWriterSettings.NamespaceHandling) | |
| 出力時の検証に関するプロパティ | XmlWriterSettings.CheckCharacters |
trueを指定した場合、CRLFを除くC0制御文字などのXMLとして不正な文字が含まれているかチェックし、含まれている場合は例外をスローする。falseを指定した場合、XMLとして不正な文字が含まれている場合はエンティティ化して出力し、例外はスローしない。(デフォルトは true: チェックする) |
§.不正な文字の処理 (XmlWriterSettings.CheckCharacters) |
| XmlWriterSettings.ConformanceLevel |
ConformanceLevel.Documentを指定すれば、完全なXMLドキュメントとして適合するかどうかの検証が行われる。 ConformanceLevel.Fragmentを指定すれば、XMLフラグメント(不完全なXMLの断片)として適合するかどうかの検証が行われる。 いずれの場合も問題があればXmlExceptionがスローされる。 (デフォルトは ConformanceLevel.Document: 完全なXMLドキュメントとして検証する) |
||
| XmlWriterの出力先に関するプロパティ | XmlWriterSettings.CloseOutput |
trueを指定した場合、XmlWriterを閉じる際に元になったStream/TextWriterも同時に閉じる。falseを指定した場合、XmlWriterを閉じても元になったStream/TextWriterは開いたままにする。 この場合、XmlWriterを使用し終わったあともStream/TextWriterを継続して用いることができる。(デフォルトは false: 元になったStream/TextWriterを開いたままにする) |
§.XmlWriterを使ったXMLツリーの文字列化 |
個々のプロパティを指定した場合の動作については以降で詳しく解説します。
エンコーディング (XmlWriterSettings.Encoding)
XmlWriterSettings.EncodingではXmlWriterで出力する際のエンコーディングを指定することができます。 デフォルトの状態ではUTF-8(BOM付き)が用いられます。 XmlWriterSettings.Encodingにnullを指定することはできません。
XmlWriterSettings.Encodingを指定した場合、XML宣言にはそのエンコーディングが設定されます。 XMLツリーにXML宣言(XDeclarationやXmlDeclaration)が含まれていてencoding属性でなんらかのエンコーディングを指定していても、XmlWriterSettings.Encodingの指定が優先され、XML宣言のencoding属性が上書きされます。
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
// XML宣言でエンコーディングにutf-8を指定する
new XDeclaration(version: "1.0", encoding: "utf-8", standalone: "yes"),
new XElement("root", "日本語")
);
// XmlWriterSettingsでエンコーディングにShift_JISを指定する
var settings = new XmlWriterSettings() {
Encoding = Encoding.GetEncoding("Shift_JIS"),
};
using (var writer = XmlWriter.Create("output.xml", settings)) {
// Shift_JISでファイル"output.xml"に出力される
doc.Save(writer);
}
}
}
<?xml version="1.0" encoding="shift_jis" standalone="yes"?><root>日本語</root>
BOM出力の制御
XDocument.Saveメソッドなどを用いて保存する場合、出力される内容の先頭にはBOM(Byte Order Mark)が付けられます。 また、XmlWriterSettings.EncodingにEncoding.UTF8を指定した場合や、XmlWriterSettings.Encodingをデフォルト値のままにした場合にもBOMが付けられます。
BOM無しのXMLを出力したい場合は、コンストラクタのパラメータencoderShouldEmitUTF8Identifierにfalseを指定したUTF8Encodingを作成し、XmlWriterSettings.Encodingに指定します。
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
new XElement("root")
);
using (var stream = new MemoryStream()) {
// エンコーディングにEncoding.UTF8を指定する
var settings = new XmlWriterSettings() {
Encoding = Encoding.UTF8,
};
using (var writer = XmlWriter.Create(stream, settings)) {
// BOM付きのUTF-8でstreamに出力される
doc.Save(writer);
}
// 出力された内容をバイト表現・文字列で表示
Console.WriteLine(BitConverter.ToString(stream.ToArray()));
Console.WriteLine(Encoding.UTF8.GetString(stream.ToArray()));
}
}
}
EF-BB-BF-3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63- 6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-72-6F-6F-74-20-2F-3E <?xml version="1.0" encoding="utf-8"?><root />
(BOM"EF-BB-BF"が先頭3バイトに出力されている)
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
new XElement("root")
);
using (var stream = new MemoryStream()) {
// エンコーディングにencoderShouldEmitUTF8Identifier=falseの
// UTF8Encodingを指定する
var settings = new XmlWriterSettings() {
Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
};
using (var writer = XmlWriter.Create(stream, settings)) {
// BOMなしのUTF-8でstreamに出力される
doc.Save(writer);
}
// 出力された内容をバイト表現・文字列で表示
Console.WriteLine(BitConverter.ToString(stream.ToArray()));
Console.WriteLine(Encoding.UTF8.GetString(stream.ToArray()));
}
}
}
3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63- 6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-72-6F-6F-74-20-2F-3E <?xml version="1.0" encoding="utf-8"?><root />
(BOM"EF-BB-BF"が出力されていない)
UnicodeEncoding(UTF-16)やUTF32Encoding(UTF-32)の場合も同様にコンストラクタの引数byteOrderMarkによってBOMの有無を指定することができます。
System.Text.EncodingとBOMの関連や、他のクラスにおけるBOM出力の制御についてはEncodingクラスとBOMありなしの制御を参照してください。
XML宣言の省略 (XmlWriterSetings.OmitXmlDeclaration)
XmlWriterSettings.OmitXmlDeclarationにtrueを指定すると、XML文書内にXDeclarationやXmlDeclarationが存在していてもXML宣言の出力を省略するようになります。
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
// XDocumentにXML宣言(XDeclaration)を含める
new XDeclaration(version: "1.0", encoding: "utf-8", standalone: "yes"),
new XElement("root",
new XElement("foo", "foo"),
new XElement("bar")
)
);
var settings = new XmlWriterSettings() {
// XmlWriterでの出力時にXML宣言の出力を省略する
OmitXmlDeclaration = true,
};
using (var writer = XmlWriter.Create("output.xml", settings)) {
// XML宣言のないXML文書が"output.xml"に保存される
doc.Save(writer);
}
}
}
<root><foo>foo</foo><bar /></root>
なお、XDocument.ToStringメソッドでは、文字列化に際してXML宣言が省略されます。
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
// XDocumentにXML宣言を含める
new XDeclaration(version: "1.0", encoding: "utf-8", standalone: "yes"),
new XElement("root",
new XElement("foo", "foo"),
new XElement("bar")
)
);
// ToStringメソッドを使ってXDocumentを文字列化する
// (この場合、XML宣言は出力されない)
Console.WriteLine(doc.ToString());
}
}
<root> <foo>foo</foo> <bar /> </root>
インデント (XmlWriterSettings.Indent, IndentChars)
XmlWriterSettings.Indentにtrueを指定すると要素のインデントと要素間の改行を行った上で出力するようになります。
インデントに用いる文字はXmlWriterSettings.IndentCharsで指定します。 IndentCharsにはタブ("\t")や半角空白(" ")、全角空白(" ")、改行文字など、ホワイトスペースとして扱われる文字(Char.IsWhiteSpace = true)からなる文字列を指定します。 それ以外の文字列を指定することもできますが、その場合は不正なXMLが出力されます。
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
new XElement("root",
new XElement("foo",
new XElement("bar", "baz")
),
new XElement("ほげ", "もげ")
)
);
var settings = new XmlWriterSettings() {
Indent = true, // 要素のインデントと要素間の改行を行う
};
// 半角空白4つ単位でインデントする
settings.IndentChars = new String(' ', 4);
using (var writer = XmlWriter.Create("output1.xml", settings)) {
doc.Save(writer);
}
// 全角空白1つ単位でインデントする
settings.IndentChars = " ";
using (var writer = XmlWriter.Create("output2.xml", settings)) {
doc.Save(writer);
}
// タブ1つ単位でインデントする
settings.IndentChars = "\t";
using (var writer = XmlWriter.Create("output3.xml", settings)) {
doc.Save(writer);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<foo>
<bar>baz</bar>
</foo>
<ほげ>もげ</ほげ>
</root>
<?xml version="1.0" encoding="utf-8"?>
<root>
<foo>
<bar>baz</bar>
</foo>
<ほげ>もげ</ほげ>
</root>
<?xml version="1.0" encoding="utf-8"?>
<root>
<foo>
<bar>baz</bar>
</foo>
<ほげ>もげ</ほげ>
</root>
改行文字の指定はXmlWriterSettings.NewLineCharsで行うことができます。
XmlWriterSettingsではインデント文字(空白やタブ)とインデント幅とを個別に指定することはできません。 あらかじめ目的のインデント幅を持った文字列をXmlWriterSettings.IndentCharsに指定する必要があります。
using System;
using System.Linq;
using System.Xml;
class Sample {
static void Main()
{
var settings = new XmlWriterSettings();
// インデント文字に半角空白、インデント幅4でインデントする
settings.IndentChars = " ";
// これは以下のようにすることもできる
settings.IndentChars = new string(' ', 4);
settings.IndentChars = string.Concat(Enumerable.Repeat(" ", 4));
// タブ2文字でインデントする
settings.IndentChars = "\t\t";
}
}
繰り返した文字列を作成する方法については文字列の加工・編集 §.乗算演算子(*, *=)・繰り返した文字列の生成を参照してください。
要素間・属性間の改行 (XmlWriterSettings.NewLineChars, NewLineOnAttributes)
XmlWriterSettings.Indentにtrueを指定した上でXmlWriterSettings.NewLineCharsを指定すると、出力時に用いる改行文字を変更することができます。
IndentCharsと同様、NewLineCharsには改行文字以外にも空白などのホワイトスペースとして扱われる文字(Char.IsWhiteSpace = trueとなる文字)からなる文字列を指定することもできます。
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
new XElement("root",
new XElement("foo",
new XElement("bar", "baz")
),
new XElement("ほげ", "もげ")
)
);
var settings = new XmlWriterSettings() {
Indent = true, // 要素のインデントと要素間の改行を行う
};
// 改行文字にCRLFを用いる
settings.NewLineChars = "\r\n";
using (var writer = XmlWriter.Create("output1.xml", settings)) {
doc.Save(writer);
}
// 改行文字に半角空白を用いる
settings.NewLineChars = " ";
using (var writer = XmlWriter.Create("output2.xml", settings)) {
doc.Save(writer);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<foo>
<bar>baz</bar>
</foo>
<ほげ>もげ</ほげ>
</root>
<?xml version="1.0" encoding="utf-8"?> <root> <foo> <bar>baz</bar> </foo> <ほげ>もげ</ほげ> </root>
XmlWriterSettings.NewLineOnAttributesにtrueを指定すると要素間だけでなく属性間にも改行を行うようにすることができます。 属性のインデントにはIndentCharsに指定されている文字列、改行にはNewLineCharsに指定されている文字列が用いられます。
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
new XElement("root",
new XAttribute("foo", "ほげ"),
new XAttribute("bar", "もげ"),
new XElement("foo")
)
);
var settings = new XmlWriterSettings() {
Indent = true, // 要素のインデントと要素間の改行を行う
};
// 各属性間に改行を入れない
settings.NewLineOnAttributes = false;
using (var writer = XmlWriter.Create("output1.xml", settings)) {
doc.Save(writer);
}
// 各属性間にも改行を入れる
settings.NewLineOnAttributes = true;
using (var writer = XmlWriter.Create("output2.xml", settings)) {
doc.Save(writer);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root foo="ほげ" bar="もげ">
<foo />
</root>
<?xml version="1.0" encoding="utf-8"?>
<root
foo="ほげ"
bar="もげ">
<foo />
</root>
属性値が長くなる場合や、要素に付与する属性の数が多数になる場合は、XmlWriterSettings.NewLineOnAttributesをtrueにして属性間を改行することによって視認性を上げることができます。
テキストノード・属性値の改行の処理 (XmlWriterSettings.NewLineHandling)
XmlWriterSettings.NewLineHandlingプロパティではテキストノード・属性値に含まれる改行文字をどのように扱うかを指定することができます。
改行文字のエンティティ化 (NewLineHandling.Entitize)
NewLineHandling.Entitizeを指定した場合は、テキストノード内における"\r"(U+000D 'CARRIAGE RETURN')がエンティティ化され、"
"として出力されます。 "\n"(U+000A 'LINE FEED')はエンティティ化されません。 テキストノード内の各改行文字がそれぞれどのようにエンティティ化されるかを表にすると次のようになります。
| XmlWriterSettings.NewLineHandlingの値 | テキストノード内の改行文字 | ||
|---|---|---|---|
\r
|
\n
|
\r\n
|
|
| NewLineHandling.None | \r | \n | \r\n |
| NewLineHandling.Entitize | 
 | \n | 
\n |
NewLineHandling.Entitizeを指定して改行文字をエンティティ化しておくことには次のような利点があります。 例えば、出力したXML文書が他のXMLプロセッサによって読み込まれる際に、テキストノード・属性値内の改行文字が空白文字として扱われることによって消失したり変わってしまうことを防ぐことができます。 この指定は、テキストノード・属性値内における改行文字が重要な意味を持つ場合に特に有用です。
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
new XElement("root", "line1\rline2\nline3\r\nline4")
);
var settings = new XmlWriterSettings();
// テキストノード内の改行文字をそのままにする
settings.NewLineHandling = NewLineHandling.None;
using (var writer = XmlWriter.Create("output-none.xml", settings)) {
doc.Save(writer);
}
// テキストノード内の"\r"(CR)をエンティティ化する
settings.NewLineHandling = NewLineHandling.Entitize;
using (var writer = XmlWriter.Create("output-entitize.xml", settings)) {
doc.Save(writer);
}
}
}
<?xml version="1.0" encoding="utf-8"?><root>line1
line2
line3
line4</root>
<?xml version="1.0" encoding="utf-8"?><root>line1
line2
line3
line4</root>
NewLineHandling.Entitizeを指定した場合は属性値も同様にエンティティ化されます。 属性値の場合、"\r"と"\n"(U+000A 'LINE FEED')の両方に加えて"\t"(U+0009 'CHARACTER TABULATION')がエンティティ化されます。 属性値内の各制御文字がそれぞれどのようにエンティティ化されるかを表にすると次のようになります。
| XmlWriterSettings.NewLineHandlingの値 | 属性値内の制御文字 | |||
|---|---|---|---|---|
\r
|
\n
|
\r\n
|
\t
|
|
| NewLineHandling.None | \r | \n | \r\n | \t |
| NewLineHandling.Entitize | 
 | 
 | 
 | 	 |
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
new XElement("root",
new XAttribute("attr", "line1\rline2\nline3\r\nline\t4")
)
);
var settings = new XmlWriterSettings();
// 属性値内の制御文字をそのままにする
settings.NewLineHandling = NewLineHandling.None;
using (var writer = XmlWriter.Create("output-none.xml", settings)) {
doc.Save(writer);
}
// 属性値内の"\r"(CR)・"\n"(LF)・"\t"(TAB)をエンティティ化する
settings.NewLineHandling = NewLineHandling.Entitize;
using (var writer = XmlWriter.Create("output-entitize.xml", settings)) {
doc.Save(writer);
}
}
}
<?xml version="1.0" encoding="utf-8"?><root attr="line1
line2
line3
line 4" />
<?xml version="1.0" encoding="utf-8"?><root attr="line1
line2
line3
line	4" />
改行文字の置換 (NewLineHandling.Replace)
NewLineHandling.Replaceを指定した場合は、テキストノード内における"\r"(U+000D 'CARRIAGE RETURN')・"\n"(U+000A 'LINE FEED')および"\r\n"のシーケンスがXmlWriterSettings.NewLineCharsで設定されている値へと置換されます。 一方、属性値内における改行文字に対しては置換が行われず、NewLineHandling.Entitizeを指定した場合と同様のエンティティ化が行われます。
テキストノード内と属性値内の各改行文字がそれぞれどのように置換されるかを表にすると次のようになります。
| XmlWriterSettings.NewLineHandlingの値 | テキストノード内の改行文字 | 属性値内の制御文字 | |||||
|---|---|---|---|---|---|---|---|
\r
|
\n
|
\r\n
|
\r
|
\n
|
\r\n
|
\t
|
|
| NewLineHandling.None | \r | \n | \r\n | \r | \n | \r\n | \t |
| NewLineHandling.Replace | NewLineChars | NewLineChars | NewLineChars | 
 | 
 | 
 | 	 |
| (XmlWriterSettings.NewLineCharsに置換される) | (NewLineHandling.Entitizeを指定した場合と同様にエンティティ化される) | ||||||
このように、NewLineHandling.Replaceを用いるとテキストノード内の改行文字を統一することができます。
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
new XElement("root", "line1\rline2\nline3\r\nline4")
);
var settings = new XmlWriterSettings();
// テキストノード内の改行文字をそのままにする
settings.NewLineHandling = NewLineHandling.None;
using (var writer = XmlWriter.Create("output-none.xml", settings)) {
doc.Save(writer);
}
// テキストノード内の改行文字を"\n"(LF)に統一する
settings.NewLineHandling = NewLineHandling.Replace;
settings.NewLineChars = "\n";
using (var writer = XmlWriter.Create("output-replace-tolf.xml", settings)) {
doc.Save(writer);
}
// テキストノード内の改行文字を"\r\n"(CRLF)に統一する
settings.NewLineHandling = NewLineHandling.Replace;
settings.NewLineChars = "\r\n";
using (var writer = XmlWriter.Create("output-replace-tocrlf.xml", settings)) {
doc.Save(writer);
}
}
}
$ od -vtx1 -a output-none.xml
0000000 ef bb bf 3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e
o ; ? < ? x m l sp v e r s i o n
0000020 3d 22 31 2e 30 22 20 65 6e 63 6f 64 69 6e 67 3d
= " 1 . 0 " sp e n c o d i n g =
0000040 22 75 74 66 2d 38 22 3f 3e 3c 72 6f 6f 74 3e 6c
" u t f - 8 " ? > < r o o t > l
0000060 69 6e 65 31 0d 6c 69 6e 65 32 0a 6c 69 6e 65 33
i n e 1 cr l i n e 2 nl l i n e 3
0000100 0d 0a 6c 69 6e 65 34 3c 2f 72 6f 6f 74 3e
cr nl l i n e 4 < / r o o t >
0000116
$ od -vtx1 -a output-replace-tolf.xml
0000000 ef bb bf 3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e
o ; ? < ? x m l sp v e r s i o n
0000020 3d 22 31 2e 30 22 20 65 6e 63 6f 64 69 6e 67 3d
= " 1 . 0 " sp e n c o d i n g =
0000040 22 75 74 66 2d 38 22 3f 3e 3c 72 6f 6f 74 3e 6c
" u t f - 8 " ? > < r o o t > l
0000060 69 6e 65 31 0a 6c 69 6e 65 32 0a 6c 69 6e 65 33
i n e 1 nl l i n e 2 nl l i n e 3
0000100 0a 6c 69 6e 65 34 3c 2f 72 6f 6f 74 3e
nl l i n e 4 < / r o o t >
0000115
$ od -vtx1 -a output-replace-tocrlf.xml
0000000 ef bb bf 3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e
o ; ? < ? x m l sp v e r s i o n
0000020 3d 22 31 2e 30 22 20 65 6e 63 6f 64 69 6e 67 3d
= " 1 . 0 " sp e n c o d i n g =
0000040 22 75 74 66 2d 38 22 3f 3e 3c 72 6f 6f 74 3e 6c
" u t f - 8 " ? > < r o o t > l
0000060 69 6e 65 31 0d 0a 6c 69 6e 65 32 0d 0a 6c 69 6e
i n e 1 cr nl l i n e 2 cr nl l i n
0000100 65 33 0d 0a 6c 69 6e 65 34 3c 2f 72 6f 6f 74 3e
e 3 cr nl l i n e 4 < / r o o t >
0000120
(ここでcr = CR, nl = new line = LF)
NewLineHandling.Replaceを指定しても属性値の改行文字がNewLineCharsに置換されることはなく、NewLineHandling.Entitizeを指定した場合と同様にすべてエンティティ化されます。 改行文字だけでなく、タブ文字もエンティティ化される点に注意してください。
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
new XElement("root",
new XAttribute("attr", "line1\rline2\nline3\r\nline\t4")
)
);
var settings = new XmlWriterSettings();
// 属性値内の改行をそのままにする
settings.NewLineHandling = NewLineHandling.None;
using (var writer = XmlWriter.Create("output-none.xml", settings)) {
doc.Save(writer);
}
// 属性値内の改行文字をエンティティ化する("\n"には置換されない)
settings.NewLineHandling = NewLineHandling.Replace;
settings.NewLineChars = "\n";
using (var writer = XmlWriter.Create("output-replace-tolf.xml", settings)) {
doc.Save(writer);
}
// 属性値内の改行文字をエンティティ化する("\r\n"には置換されない)
settings.NewLineHandling = NewLineHandling.Replace;
settings.NewLineChars = "\r\n";
using (var writer = XmlWriter.Create("output-replace-tocrlf.xml", settings)) {
doc.Save(writer);
}
}
}
<?xml version="1.0" encoding="utf-8"?><root attr="line1
line2
line3
line 4" />
<?xml version="1.0" encoding="utf-8"?><root attr="line1
line2
line3
line	4" />
<?xml version="1.0" encoding="utf-8"?><root attr="line1
line2
line3
line	4" />
重複する名前空間宣言の処理 (XmlWriterSettings.NamespaceHandling)
XmlWriterSettings.NamespaceHandlingプロパティでは、XMLツリー内に重複する名前空間宣言(xmlns属性)が存在する場合にそれらをどのように扱うかを指定することができます。
NamespaceHandling.OmitDuplicatesを指定した場合は、重複する名前空間宣言を削除し、最も上位にある名前空間宣言だけを残します。 この際、名前空間とプレフィックスの両方が一致する名前空間宣言のみが削除されます。 プレフィックスが異なる名前空間宣言は別のものとして扱われ、省略されることはありません。
NamespaceHandling.Defaultを指定した場合は、名前空間宣言が重複していてもそのままにします。
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var nsAtom = (XNamespace)"http://www.w3.org/2005/Atom";
var doc = new XDocument(
new XElement(nsAtom + "feed",
new XAttribute(XNamespace.Xmlns + "atom", nsAtom.NamespaceName),
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
new XElement(nsAtom + "title", "更新履歴 (Atom 1.0)"),
new XElement(nsAtom + "entry",
// 上と重複する名前空間の宣言
new XAttribute(XNamespace.Xmlns + "atom", nsAtom.NamespaceName),
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
new XElement(nsAtom + "title",
new XAttribute("type", "text"),
"entry 1"
)
)
)
);
var settings = new XmlWriterSettings() {
Indent = true,
};
// 重複する名前空間の宣言を省略する
settings.NamespaceHandling = NamespaceHandling.OmitDuplicates;
using (var writer = XmlWriter.Create("output-omitduplicates.xml", settings)) {
doc.Save(writer);
}
// 重複する名前空間の宣言をそのままにする
settings.NamespaceHandling = NamespaceHandling.Default;
using (var writer = XmlWriter.Create("output-default.xml", settings)) {
doc.Save(writer);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<atom:feed xmlns:atom="http://www.w3.org/2005/Atom">
<atom:title>更新履歴 (Atom 1.0)</atom:title>
<atom:entry>
<atom:title type="text">entry 1</atom:title>
</atom:entry>
</atom:feed>
<?xml version="1.0" encoding="utf-8"?>
<atom:feed xmlns:atom="http://www.w3.org/2005/Atom">
<atom:title>更新履歴 (Atom 1.0)</atom:title>
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
<atom:title type="text">entry 1</atom:title>
</atom:entry>
</atom:feed>
次の例のように、名前空間が同一でもプレフィックスが異なっている場合はそれぞれ個別の名前空間宣言とみなされ、NamespaceHandling.OmitDuplicatesを指定しても名前空間宣言の省略は行われません。
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var nsAtom = (XNamespace)"http://www.w3.org/2005/Atom";
var doc = new XDocument(
new XElement(nsAtom + "feed",
new XAttribute(XNamespace.Xmlns + "atom", nsAtom.NamespaceName),
// ~~~~~~ ~~~~~~
new XElement(nsAtom + "title", "更新履歴 (Atom 1.0)"),
new XElement(nsAtom + "entry",
// 上の名前空間宣言と同一名前空間でもプレフィックスは異なるので
// NamespaceHandling.OmitDuplicatesを指定しても省略されない
new XAttribute(XNamespace.Xmlns + "a", nsAtom.NamespaceName),
// ~~~ ~~~~~~
new XElement(nsAtom + "title",
new XAttribute("type", "text"),
"entry 1"
)
)
)
);
var settings = new XmlWriterSettings() {
Indent = true,
};
// 重複する名前空間の宣言を省略する
settings.NamespaceHandling = NamespaceHandling.OmitDuplicates;
using (var writer = XmlWriter.Create("output-omitduplicates.xml", settings)) {
doc.Save(writer);
}
// 重複する名前空間の宣言をそのままにする
settings.NamespaceHandling = NamespaceHandling.Default;
using (var writer = XmlWriter.Create("output-default.xml", settings)) {
doc.Save(writer);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<atom:feed xmlns:atom="http://www.w3.org/2005/Atom">
<atom:title>更新履歴 (Atom 1.0)</atom:title>
<a:entry xmlns:a="http://www.w3.org/2005/Atom">
<a:title type="text">entry 1</a:title>
</a:entry>
</atom:feed>
<?xml version="1.0" encoding="utf-8"?>
<atom:feed xmlns:atom="http://www.w3.org/2005/Atom">
<atom:title>更新履歴 (Atom 1.0)</atom:title>
<a:entry xmlns:a="http://www.w3.org/2005/Atom">
<a:title type="text">entry 1</a:title>
</a:entry>
</atom:feed>
XDocument.Saveメソッドを用いる場合は、SaveOptions.OmitDuplicateNamespacesを使うことでもこのプロパティと同じ結果を得ることができます。
不正な文字の処理 (XmlWriterSettings.CheckCharacters)
XmlWriterSettings.CheckCharactersプロパティでは、XML内にC0制御文字(CR・LFを除く)など、XMLとして不正な文字が含まれているかをチェックするかどうかを指定することができます。
trueを指定してチェックするようにした場合、不正な文字が含まれている際には出力時に例外ArgumentExceptionをスローします。
falseを指定してチェックしないようにした場合、不正な文字が含まれている際にはそれをエンティティ化して出力します。 例外はスローしません。
using System;
using System.Xml;
using System.Xml.Linq;
class Sample {
static void Main()
{
var doc = new XDocument(
new XElement("root",
// U+0000 'NULL'で終端されているテキストノード
new XText("text\0")
)
);
var settings = new XmlWriterSettings();
// XMLとして不正な文字列が含まれているかチェックしないで出力する
// (不正な文字がある場合はエンティティ化して出力する)
settings.CheckCharacters = false;
Console.WriteLine("[CheckCharacters = false]");
using (var writer = XmlWriter.Create("output-unchecked.xml", settings)) {
doc.Save(writer);
}
Console.WriteLine("done");
// XMLとして不正な文字列が含まれているかチェックして出力する
// (不正な文字がある場合は例外をスローする)
settings.CheckCharacters = true;
Console.WriteLine("[CheckCharacters = true]");
using (var writer = XmlWriter.Create("output-checked.xml", settings)) {
doc.Save(writer);
}
Console.WriteLine("done");
}
}
[CheckCharacters = false] done [CheckCharacters = true] ハンドルされていない例外: System.ArgumentException: '.' (16 進数値 0x00) は無効な文字です。 場所 System.Xml.XmlUtf8RawTextWriter.InvalidXmlChar(Int32 ch, Byte* pDst, Boolean entitize) 場所 System.Xml.XmlUtf8RawTextWriter.WriteElementTextBlock(Char* pSrc, Char* pSrcEnd) 場所 System.Xml.XmlUtf8RawTextWriter.WriteString(String text) 場所 System.Xml.XmlWellFormedWriter.WriteString(String text) 場所 System.Xml.Linq.ElementWriter.WriteElement(XElement e) 場所 System.Xml.Linq.XElement.WriteTo(XmlWriter writer) 場所 System.Xml.Linq.XContainer.WriteContentTo(XmlWriter writer) 場所 System.Xml.Linq.XDocument.WriteTo(XmlWriter writer) 場所 Sample.Main()
<?xml version="1.0" encoding="utf-8"?><root>text�</root>