この文書では、System.Xml.Linq名前空間のクラス郡を使ってXML文書を構築する方法、またファイルなどに出力する方法ついて扱います。 XDocumentクラスやXElementクラスを使ったXMLの構築では、System.Xml.XmlDocumentクラスを使った場合と比べるとXMLの構造にごく近い形で記述でき、コードが非常に分かりやすくなります。 インターフェイスもシンプルで把握しやすく、目的とするコードを手早く記述することができます。
System.Xml.Linq名前空間のクラスはアセンブリSystem.Xml.Linq.dllに含まれています。 使用する際はSystem.Xml.Linq.dllを参照に追加します。
この文書では、XMLの読み込みと加工、XMLからのデータの読み出し、またLINQ to XMLの機能については扱いません。
概略
System.Xml.Linq名前空間のクラス郡を用いてXML文書の構築を行う場合は、XDocumentクラスやXElementクラスなどを使います。 これらのクラスを入れ子にしていくことで文書構造を構成します。
XDocument
がXML文書、XElement
が要素を表すクラスとなります。 XElementのコンストラクタでは、new XElement(要素名, 子ノード, 子ノード, ...)
のように引数を指定します。 これにより、要素に任意個の子ノードを含めることができます。
子ノードとしてXElementを指定すれば子要素となり、これを入れ子にしていくことで文書構造を定義していくことができます。 XElementではなく文字列を子ノードに指定すれば、テキストノード、つまり要素内のテキストを指定することができます。 また、子ノードとしてXAttributeやXCommentを含めれば、要素に属性やコメントを付与することができます。
生成したXML文書をファイルなどに保存する場合はXDocument.Saveメソッドを使います。
Saveメソッドでの保存時に指定できるオプションについては§.Saveメソッドを用いた出力を参照してください。
インデントの有無・インデント幅の変更、文字コードの扱いなど、出力時の動作を変更する方法については後述の§.XmlWriterを用いた出力を参照してください。
XMLツリーの構築
XMLツリーを構築する際には、System.Xml.Linq名前空間のクラスを組み合わせて構成します。 以下はその主なクラスです。
ノードの種類・構成要素 | 該当するクラス | 解説 |
---|---|---|
XML文書 | XDocument | - |
要素 | XElement | §.要素・属性・テキストノード (XElement・XAttribute・XText) |
属性 | XAttribute | §.要素・属性・テキストノード (XElement・XAttribute・XText) |
テキスト | XText | §.要素・属性・テキストノード (XElement・XAttribute・XText) |
CDATAセクション | XCData | §.CDATAセクション (XCData) |
コメント | XComment | §.コメントノード (XComment) |
XML宣言 | XDeclaration | §.XML宣言 (XDeclaration) |
名前空間 | XNamespace | §.名前空間 (XNamespace) |
要素・属性・テキストノード (XElement・XAttribute・XText)
要素を作成するにはXElementクラス、属性を作成するにはXAttributeクラスを使います。
XElementコンストラクタ・XAttributeコンストラクタの第一引数は要素・属性の名前を指定します。 XElementコンストラクタでは、第二引数以降には子要素・要素のテキスト・属性を指定することができ、それぞれ指定した順序で要素に追加されます。 第二引数以降を指定しなければ空の要素(<element/>
)が作成されます。 XAttributeコンストラクタでは、第二引数に属性の値を指定します。
要素の作成 |
new XElement(要素名, 子ノード, 子ノード, ...)
|
空の要素の作成 |
new XElement(要素名)
|
属性の作成 |
new XAttribute(属性名, 属性値)
|
XDocumentは子要素(XElement)を一つだけ持つことができます。 XMLではルート要素(他の要素に格納されない最上位の要素)は常に1つだけです。 複数のルート要素を持たせることはできません。 XDocumentのルート要素はXDocument.Rootプロパティを使って取得することができます。
XElementコンストラクタの第二引数以降に文字列を指定すると、それが要素のテキストノードとして格納されます。 一方、テキストノードを作成するためにXTextクラスを用いることもできます。 XElementコンストラクタの引数に文字列を指定した場合は、それが自動的にXTextへと変換され、XElementの子ノードとして追加されます。
文字列を使った作成 |
new XElement(要素名, "テキスト", "テキスト", ...)
|
XTextを使った作成 |
new XElement(要素名, new XText("テキスト"), new XText("テキスト"), ...)
|
したがって、以下のコードはどちらも同じXMLツリーを生成します。
このように、テキストノードを単に文字列で記述した場合とXTextクラスを用いて記述した場合では結果に違いはありません。 また、XTextクラスを用いてテキストノードを記述するとコードの記述量が増えるというデメリットはありますが、一方で単に文字列で記述した場合と比べるとそれがテキストノードであることを分かりやすくすることができるというメリットもあります。
CDATAセクションを用いてテキストノードを記述したい場合はXCDataクラスを用います。
詳細は後述の§.要素のテキスト・属性の値で解説しますが、XElementでは数値など文字列以外の値を指定した場合はそれが自動的にテキストノードとして追加されます。 一方、XTextコンストラクタでは文字列のみが指定できるため、数値などを指定することはできません。
要素のテキスト・属性の値
XElement・XAttributeコンストラクタの第二引数以降はObject型になっていて、要素のテキスト・属性値として文字列だけでなく数値やDateTimeOffset型、Uriクラスなどの値を直接指定することができます。
コンストラクタで要素のテキスト・属性値として指定した値は、基本的にはToStringメソッドによって文字列化した場合と同じフォーマットで出力されますが、日時型(DateTime型・DateTimeOffset型)を指定した場合はW3C-DTF形式(ISO8601形式)でフォーマットされます。 それ以外の書式で出力したい場合はToStringメソッドで目的の書式を指定して文字列化する必要があります。
XElementコンストラクタで連続する複数のテキストを指定した場合は、上記のように一つの連結したテキストとして出力されます。 間に空白や区切り文字などが挟まれることはありません。
DateTimeOffsetを文字列化する際の書式については書式指定子 §.日付と時刻の書式指定子を参照してください。
TimeSpan型の値も日時と同様にISO8601形式の時間長(duration)として出力されます。 これ以外の書式で出力したい場合はToStringメソッドで目的の書式を指定して文字列化する必要があります。
出力される時間長の書式は次のようになっています。
記号 | 意味 |
---|---|
P
|
以降の文字列が時間長で表される期間(Period)であることを表す |
nD
|
日数(Days)を表す |
T
|
以降の文字列が時間長の時刻部分であることを表す ( P<日数>T<時間> ) |
nH
|
時間数(Hours)を表す |
nM
|
分数(Minutes)を表す |
nS
n.mS
|
秒数(Seconds)を表す 端数(ミリ秒)がある場合は小数点数となる |
TimeSpanを文字列化する際の書式については書式指定子 §.時間間隔の書式指定子を参照してください。
子ノードの追加 (XElement.Add)
XDocumentやXElementでは、インスタンス作成時にコンストラクタで子ノードを格納するほかにも、インスタンス作成後にAddメソッドなどを使って子ノードを追加していくこともできます。
Addメソッドの引数もコンストラクタと同様にObject型となっているため、§.要素のテキスト・属性の値と同じように要素に対してテキストノードや属性を追加する場合にもこのメソッドを使うことができます。
Addメソッドのほかにも、ノードを追加したい位置に応じて次のメソッドを使うことができます。
メソッド | 動作 |
---|---|
Add(node) | nodeを子ノードの末尾に追加する |
AddFirst(node) | nodeを子ノードの先頭に追加する |
AddBeforeSelf(node) | nodeを自身の直前に追加する(兄ノードとして追加する) |
AddAfterSelf(node) | nodeを自身の直後に追加する(弟ノードとして追加する) |
これらのメソッドはXContainerクラスによって提供され、このクラスを継承しているXDocumentやXElementでは共通してこのメソッドを使うことができます。
これらのメソッドでノードが追加される位置を実際のコードを使って比較すると次のようになります。
名前空間 (XNamespace)
この項での名前空間とは、プログラム要素としての名前空間(System.Xml.Linq名前空間など)ではなく、XML要素の名前空間(xmlns
)のことです。 混同しないように注意してください。
名前空間付きの要素を作成したい場合は、XNamespaceクラスを使います。 XElementコンストラクタの第一引数に名前空間なしの要素名を指定するかわりに、次のようにXNamespace + 要素名
と指定することによって要素に名前空間を与えた上でXMLツリーに追加することができます。
名前空間のない要素の作成 |
new XElement(要素名)
|
名前空間付きの要素の作成 |
new XElement(XNamespace + 要素名)
|
XNamespace + 要素名
という式からはXNameクラスのインスタンスが作成されます。 XNameクラスについて詳しくは§.名前 (XName)で解説します。
XNamespaceのインスタンスは、名前空間名の文字列を型変換することで作成します。 コンストラクタを使った作成はできない点に注意してください。
上記のように、要素に与えた各名前空間はプレフィックスなしのデフォルト名前空間として出力されます。 同一スコープ内にある同一名前空間の要素は自動的に弁別されるため、すべての要素に名前空間を指定しても、そのすべてにxmlns
属性が付与されることはありません。
名前空間付きの要素を作成する場合、XElementコンストラクタの第一引数に"{名前空間}要素名
"のような展開された名前を指定することもできます。 ただし、このような指定は名前空間と要素名の解析処理が必要となり非効率であるため、推奨される方法ではありません。 特別な理由がない限りはXNamespaceクラスを使用するようにしてください。
名前空間のプレフィックス
名前空間を指定した要素を、名前空間プレフィックス(接頭辞)付きの要素名(qualified name)で出力したい場合は、XAttributeを使って要素にxmlns
属性を付与して名前空間とプレフィックスの宣言をします。 例えばnew XAttribute(XNamespace.Xmlns + "ex", "http://example.com/")
とすれば、xmlns:ex="http://example.com/"というxmlns
属性を表すXAttributeインスタンスが作成されます。
名前空間宣言となるxmlns属性 | 名前空間宣言を作成するためのXAttributeインスタンス |
---|---|
xmlns:ex="http://example.com/" |
new XAttribute(XNamespace.Xmlns + "ex", "http://example.com/")
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" |
new XAttribute(XNamespace.Xmlns + "xsl", "http://www.w3.org/1999/XSL/Transform")
|
xmlns:atom="http://www.w3.org/2005/Atom" |
new XAttribute(XNamespace.Xmlns + "atom", "http://www.w3.org/2005/Atom")
|
このような属性をもつ要素では、その要素と、要素内にある同一名前空間の要素すべてに対して自動的にプレフィックスが付与されます。
上記の例にあるように、名前空間名はXNamespace.NamespaceNameプロパティより参照することができるため、何度も同じ名前空間名を文字列で記述する必要がなくなります。
プレフィックス付きの属性名を作成する場合も要素と同様に行うことができます。
プレフィックスとデフォルト名前空間の併用
特定の名前空間にプレフィックスを付けたくない場合、必要な要素・属性だけにプレフィックスを付けたい場合は、デフォルト名前空間と併用することができます。 例えばルート要素と同じ名前空間の要素にはプレフィックスを付与したくないといった場合には、プレフィックスの宣言をしないことでデフォルト名前空間として出力させることができます。
名前 (XName)
XNameクラスは要素や属性の名前を扱うクラスで、名前空間名とローカル名(名前のうち、名前空間によって修飾される部分)をセットで保持します。 XElementやXAttributeのコンストラクタの第一引数で指定した要素・属性の名前はXNameに変換され、各クラスで保持されます。 名前空間のない要素名・属性名の場合もXNameとして保持されます。
次の例のように、要素名・属性名となるXNameのインスタンスを事前に作成しておき、要素・属性を作成する際にXElementとXAttributeに与える名前として直接XNameを指定することができます。 特に、名前空間を多く扱うXML文書ではXNamespace + "要素名・属性名"
を記述することが多くなりますが、そのような場合にXNameを使うと記述を簡略化できます。
比較のためXNameを用いた場合とそうでない場合を併記していますが、上記のいずれも同じXMLツリーを生成します。
このようにXNamespace + "ローカル名"
とすれば名前空間付きの名前を表すXNameが作成されます。 名前空間なしの名前の場合は、ローカル名の文字列を直接XNameにキャストするか、名前空間が無いことを表すXNamespace.Noneプロパティとローカル名を連結して作成します。
XNameを作成する構文 | 作成されるXNameの展開名 | |
---|---|---|
名前空間付き |
(XNamespace)"http://example.com/ns" + "localname"
|
{http://example.com/ns}localname |
名前空間なし |
(XName)"localname"
|
localname |
XNamespace.None + "localname"
|
localname |
要素名・属性名はそれぞれXElement.NameプロパティとXAttribute.NameプロパティにXNameの形で格納されます。 XName.Namespaceプロパティを参照すれば名前空間名、XName.LocalNameプロパティを参照すればローカル名を取得することができます。 XName.ToStringメソッドなどによって文字列化した場合は、名前空間が展開された名前を取得することができます。
XNameクラスではインスタンスがキャッシュされ、同一名前空間かつ同一ローカル名の名前を持つXNameを作成すると常に同一のインスタンスが返されます。 例えば次のように要素ごとに名前を指定してXNameを作成した場合でも、あらかじめ作成したXNameを用いた場合でも、それらはすべて同一のインスタンスが用いられます。
特殊なノード
XML宣言 (XDeclaration)
XML宣言を明示的に設定したい場合は、XDeclarationクラスを使って指定します。
Saveメソッドなどによって出力する場合、XDeclarationが無くてもXML宣言が出力されます。 一方、ToStringメソッドによって文字列化した場合は、XDeclarationの有無に関わらずXML宣言は出力されません。
Saveメソッドによる出力時オプションについては§.Saveメソッドを用いた出力、またXML宣言と出力時の文字コードの扱いなどについてはXmlWriterSettings §.エンコーディング (XmlWriterSettings.Encoding)を参照してください。
XML宣言はXDocument.Declarationプロパティを使うことによっても取得・設定することができます。
XDocumentに複数のXML宣言を持たせることはできません。 XDocumentに複数のXDeclarationを持たせようとした場合、ArgumentExceptionがスローされます。
DOCTYPE宣言 (XDocumentType)
DOCTYPE宣言(ドキュメントタイプ)を記述する場合は、XDocumentTypeクラスを使います。
XML宣言と同様、XDocument.DocumentTypeプロパティを使ってDOCTYPE宣言を参照することはできますが、このプロパティは読み取り専用プロパティであるため設定することはできません。
XDocumentに複数のDOCTYPE宣言を持たせることはできません。 XDocumentに複数のXDocumentTypeを持たせようとした場合、InvalidOperationExceptionがスローされます。
XML処理命令 (XProcessingInstruction)
XML処理命令(Processing Instruction)を記述する場合は、XProcessingInstructionクラスを使います。
コメントノード (XComment)
XMLツリー内にコメントを記述する場合は、XCommentクラスを使います。 XElementクラス等とは異なり、XCommentコンストラクタの引数に指定できるのは常にコメントとなる単一の文字列だけです。 複数の文字列や数値を指定したりすることはできないため、あらかじめString.FormatメソッドやString.Joinメソッドなどによってフォーマット・結合しておく必要があります。
コメントノードはルート要素の前後を含む任意の位置に追加することができます。
XCommentクラスでは、"<!--
"や"-->
"などのコメント開始・終了のマークを含む文字列もコメントとして指定することができます。 この場合、ArgumentExceptionなどの例外はスローされず、ハイフン"-
"が2つ以上連続している個所に対して自動的に空白が挿入された上でコメントノードが作成されます。
なお、System.Xml.XmlDocument.CreateCommentメソッドでは、このようなコメントノードを追加しようとした場合にはArgumentExceptionがスローされます。
XmlDocumentとXDocumentを並行して扱う場合や実装の書き換えを行う際には動作が異なる点に注意してください。
CDATAセクション (XCData)
テキストノード内にCDATAセクションを記述する場合は、XCDataクラスを使います。 CDATAセクションを用いると、テキストノード内で<
、>
、"
といった記号を文字参照形式ではなくそのまま記述することができます。 XCommentコンストラクタと同様、XCDataコンストラクタも引数に指定できるのは常にCDATAセクションとなる単一の文字列だけです。 複数の文字列や数値を指定したりすることはできないため、あらかじめ結合しておく必要があります。
一つの要素内に通常のテキストとCDATAセクションを混在させることもできます。
このようにして出力されたXMLが読み込まれる際には、テキストノードとCDATAセクション内のテキストが連結された状態で読み込まれます。 つまり、上記のXML文書における<root>
要素はテキスト"foobarbaz"
を持っている状態となります。
XCDataクラスでは、CDATAセクションの開始・終了のマーク("<![CDATA[
"および"]]>
")を含む文字列もCDATAセクションの文字列として指定することができます。 この場合、ArgumentExceptionなどの例外はスローされません。 また、終了マーク"]]>
"に関しては、"]]
"と">
"がそれぞれ個別のCDATAセクションに分割された状態で作成されます。
XMLフラグメント
XMLフラグメントの構築
次のようにXElementだけを用いればXMLフラグメント(不完全なXML文書の断片)を構築することもできます。
XElementクラスもXDocumentクラス同様のSaveメソッドを持っているため、XMLフラグメントをファイルなどに保存することができます。 この際、デフォルトではXML宣言を含んだ状態で保存されます。
XML宣言を含まずに保存したい場合はXmlWriterを使います。 詳しくはXmlWriterSettings §.XML宣言の省略 (XmlWriterSetings.OmitXmlDeclaration)を参照してください。
XMLフラグメントのインポート (XElement.Parse)
XElement.Parseメソッドを用いると、文字列で記述されたXMLフラグメントを読み込んでXElementをルートとするXMLツリーを構築することができます。
XDocumentクラスにもXDocument.Parseメソッドが用意されているため、XDocumentクラスでも文字列からXML文書を構築することができます。
XElement.Parseメソッドで読み込めるのは一つの要素をルートとするXMLフラグメントのみです。 ルートに複数の要素があるようなXMLフラグメントを読み込むことはできません。
ルートに複数の要素を含むXMLフラグメントを読み込みたい場合は、以下のように仮のルート要素で包んだ上で読み込むようにします。 その後、XElement.Elements()メソッドを使って読み込んだルート要素内に含まれる子要素だけを取り出します。
名前空間を持つXMLフラグメントをインポートしてXML文書を組み立てる場合、各XMLフラグメントで宣言している名前空間が重複する場合があります。 例えば次のようなコードでXML文書を構築した場合、同じ名前空間の宣言が複数箇所に存在するようになります。
このような場合はSaveメソッドの引数でSaveOptions.OmitDuplicateNamespacesを指定することにより、保存する際に重複する名前空間宣言を省略させることができます。 詳しくは§.SaveOptions.OmitDuplicateNamespacesを参照してください。
csprojファイル(プロジェクトファイル)のフォーマット等についてはプロジェクトファイルを参照してください。
XMLの出力
ToStringメソッドによる文字列化
XDocumentやXElementに対してToStringメソッドを呼びだせばXMLツリーを文字列化して取得することができます。 ToStringメソッドで文字列化した場合、XML宣言は省略されます。
インデントの有無やインデント幅を指定するなど、フォーマットを細かく指定して文字列化したい場合は、XmlWriterとXmlWriterSettingsを使う必要があります。 これについてはXmlWriterSettings §.XmlWriterを使ったXMLツリーの文字列化で解説しています。
Saveメソッドを用いた出力
XDocument.Saveメソッド・XElement.Saveメソッドを用いればファイルやストリームへの出力を行うこともできます。 Saveメソッドを使って出力した場合、XElement(=XMLフラグメント)の場合でもXML宣言が付与された状態で出力されます。
Saveメソッドでの出力時の動作を変更するにはSaveOptionsを指定します。 また、インデント幅や文字コードの指定、XML宣言の省略など、フォーマットを細かく制御したい場合はXmlWriterとXmlWriterSettingsを使って出力します。
Saveメソッドでは、文字コードにUTF-8などを用いて出力する場合にはBOM(Byte Order Mark)が付けられます。 BOMなしで出力したい場合についてはXmlWriterSettings §.BOM出力の制御を参照してください。
SaveOptions
XDocumentやXElementのToStringメソッド、Saveメソッドでは、出力時の動作を変えるオプションとしてSaveOptions列挙体の値を指定することができます。 SaveOptionsでは、次のいずれかの値を指定することができます。
SaveOptionsの値 | 出力時の動作 | 解説 |
---|---|---|
SaveOptions.None | SaveOptionsを指定しなかった場合のデフォルトと同じ。 | - |
SaveOptions.DisableFormatting | フォーマットを行わずに出力する。 インデントや改行など、(XML文書としての)意味を持たない要素間のホワイトスペースなどが省略された状態で出力する。 |
解説へ |
SaveOptions.OmitDuplicateNamespaces | 重複する名前空間宣言(xxx:xmlns="yyy" )がある場合は、それを削除した上で出力する。 |
解説へ |
SaveOptions.DisableFormatting
SaveOptions.DisableFormattingを指定した場合、出力に際して要素のインデントや要素間の改行は行われなくなります。
XDocument.LoadメソッドやXDocument.Parseメソッドを使ってXML文書を読み込む際にLoadOptions.PreserveWhitespace
を指定した場合、SaveOptions.None
やSaveOptions.DisableFormatting
を指定してもその指定に関わらず元のホワイトスペースが維持された状態で出力されます。
SaveOptions.OmitDuplicateNamespaces
SaveOptions.OmitDuplicateNamespacesを指定した場合、出力に際して重複する名前空間宣言が削除されます。
これは例えば、XML文書を構築する際に、その一部として他のメソッドやライブラリが生成するXMLツリーを組み込むような場合に有効になる場合があります。 個々のXMLツリーが名前空間宣言を含んでいても、それを組み込む親要素の側で名前空間宣言をしていれば、SaveOptions.OmitDuplicateNamespaces
を指定することによって同一の名前空間宣言を省略させるようにすることができます。
SaveOptions.OmitDuplicateNamespaces
で省略されるのは名前空間とそのプレフィックスが一致する場合に限られます。 次のように同一名前空間でも異なるプレフィックスを用いている場合は、SaveOptions.OmitDuplicateNamespaces
を指定していても名前空間宣言は省略されません。
XmlWriterを使った出力の場合、XmlWriterSettings.NamespaceHandlingプロパティにNamespaceHandling.OmitDuplicatesを指定すると、SaveOptions.OmitDuplicateNamespacesを指定した場合と同じ動作になります。
XmlWriterを用いた出力
XDocument.SaveメソッドおよびXElement.Saveメソッドでは、XmlWriterを使った保存もサポートしています。 XmlWriterSettingsと組み合わせて使うことで、出力時のフォーマットや動作を制御することができます。
XmlWriterおよびXmlWriterSettingsは名前空間System.Xmlのクラスで、アセンブリSystem.Xml.dll
を参照に追加することによって使うことができます。 XDocumentなどが定義されているSystem.Xml.Linq.dll
とは別アセンブリのクラスである点に注意してください。
XmlWriterの作成はXmlWriter.Createメソッドを使います。 第一引数には出力先となるファイル名を指定できるほか、StreamやTextWriterを指定することもできるため、例えばMemoryStreamを用いればXMLツリーをメモリ上に展開することもできます。 StringWriterを出力先とすれば、出力結果を文字列として取得することもできます。
XmlWriterを使ったXMLの出力と、XmlWriterSettingsで設定可能な内容についてはXmlWriterSettingsでより詳しく解説します。