XML整形ツール
ブラウザ内で整形され、送信されることはありません。テキストと要素が混在する内容は改行やインデントを加えると意味が変わるため、インラインのまま保持します。属性の順序と引用符も保持し、XML宣言を勝手に追加することもありません。
入力した内容が保存・記録・共有されることはありません。
XMLを貼り付けると、読みやすくインデントしたり1行に圧縮したりでき、整形式かどうかも確認できます。エラーは正確な行と列を示します。処理はすべてブラウザ内で行われ、データが送信されることはありません。
使い方
- XMLを入力欄に貼り付けます。
- 「整形」または「圧縮」を選び、必要ならインデント幅を設定します。
- 結果をコピーするか、エラーの行と列を確認します。
「整形式」と「妥当」は別物
XMLには正しさの基準が2つあり、しばしば混同されます。整形式(well-formed)とは構文に従っていることです。ルート要素が1つだけ、すべてのタグが対応する終了タグで閉じられ、属性値は引用符で囲まれ、コメントとCDATAが終了し、ルートの外に余分なテキストがない状態を指します。これはドキュメント単体で確認でき、このツールが報告するのもこの点です。
妥当(valid)とは、それに加えてスキーマ(DTD、XSD、RelaxNGなど)に適合していることです。スキーマは、どの要素がどの順序で何回出現してよいか、属性値がどんな形式であるべきかを定めます。この検証はスキーマなしには不可能で、構文上は完璧な整形式でも、準拠をうたう文法に照らせば無意味なドキュメントということは十分ありえます。
名前空間はそのどちらとも少し別の問題です。対応するxmlns宣言のないプレフィックスは、整形式エラーではなく名前空間エラーです。そのため構文チェックは通っても、名前空間を扱うパーサーには拒否されることがあります。SOAPやOfficeのドキュメントがきれいに整形できるのに受け付けられない場合は、まず未宣言のプレフィックスを疑ってみてください。
空白がデータになる場合がある
これが整形処理全体を支配するルールです。要素と要素の間の空白はほぼ常にレイアウトにすぎず、別のレイアウトに置き換えても何も変わりません。一方、混合内容(段落の中に太字の単語があるように、テキストと要素が兄弟として並ぶもの)の中の空白はテキストの一部であり、折り返すとドキュメントそのものが変わります。
そのため混合内容を含む要素は、インデントされたドキュメントの途中に長い行が残るとしても、書かれたとおりに保持します。多くの整形ツールはその逆で、すべてをインデントし、Helloと<b>there</b>の間に改行を入れてしまいます。その結果、表示すると元にはなかった隙間ができます。見た目は整っていても、結果は誤りです。
仕様にはこれを明示的に宣言するためのxml:space="preserve"があり、テキスト内容が意味を持つ要素には付けておく価値があります。この指定は上書きされるまで子孫要素に継承される点に注意してください。ルートに一度付けただけのつもりで驚く人は少なくありません。
XMLが壊れる本当の原因
圧倒的に多いのはエスケープされていないアンパサンドです。XMLでは&は実体参照の開始を意味するため、URLのクエリ文字列や社名にそのまま含まれていると整形式ではなくなります。&と書く必要があり、テキスト中の小なり記号は<と書かなければなりません。定義済みの実体はamp、lt、gt、quot、aposの5つだけで、それ以外は宣言するか数値文字参照で書く必要があります。
次に多いのが文字コードの不一致です。ファイル先頭の宣言はバイト列のエンコーディングを示しますが、後から別の文字コードで保存されていると、パーサーはマルチバイト文字を化けた文字として読むか、そのまま失敗します。宣言の前にあるBOMが原因で失敗するパーサーもあります。関連して、XML 1.0ではいくつかの制御文字がエスケープしても一切使えません。テキストフィールドに紛れ込んだバイナリデータはエンコードでは救えず、削除するかBase64化する必要があります。
そしてHTMLの癖です。引用符のない属性値、<br>のような閉じていないタグ、重複した属性は、HTMLでは問題なくてもXMLでは致命的です。HTML由来のドキュメントなら、まずこれらを確認してください。
実体参照と、信頼できないXMLの解析について
XMLではドキュメント自身がDTDの中で実体を宣言でき、ファイルパスやURLを指す外部実体も宣言できます。これはよく知られた2種類の攻撃の土台になっており、XMLを書かずに読むだけの立場でも知っておく価値があります。
XML外部実体(XXE)攻撃は、ローカルファイルを指す実体を宣言して、サーバーにそのファイルを読ませ、内容をレスポンスに含めさせるものです。アップロードされたXMLを既定の設定で解析するシステムから、設定ファイルや認証情報を抜き出す確実な手口として長く使われてきました。Billion Laughs攻撃は実体を入れ子にして、それぞれが次の実体を複数回展開するようにし、数キロバイトの入力をメモリ上で数ギガバイトに膨らませます。
どちらも防御策は、パーサーでDTD処理と外部実体の解決を無効にすることです。現在の主要なライブラリの多くは既定で安全になっていますが、古いコードにはそうでないものも多く残っています。このページは外部のものを一切解決しません。ドキュメントをテキストとして読むだけで、参照先を取得することはありません。
名前空間とプレフィックスの関係
名前空間はプレフィックスをURIに結び付けるものであり、要素を識別しているのはURIのほうです。プレフィックスはあくまでローカルな略記で、自由に付け替えられます。同じURIに対して soap: と s: を使っている2つの文書は等価ですし、どちらも ns1: を使っていてもURIが違えば別物です。URIではなくプレフィックスで一致判定をしているコードは、誰かが文書を整形し直すまでは動きます。
xmlns で宣言したデフォルト名前空間は要素には適用されますが、属性には適用されません。ここが多くの人が取り違えるところです。プレフィックスの付いていない属性は、囲んでいる要素が何を宣言していようと、どの名前空間にも属しません。xsi:type や xml:lang が必ずプレフィックス付きで書かれるのはそのためです。
一見整形式に見える文書をパーサーが受け付けない場合、原因はたいてい未宣言のプレフィックスです。プレフィックスの束縛は構文規則ではなく名前空間の規則なので、整形自体は成功してしまい、問題はその文書を処理する側で後から現れます。
スキーマ検証で得られるもの
整形式かどうかは構文の話で、妥当かどうかは文法への適合の話です。DTDは最初の仕組みで、機能は限られていますが実体宣言のために今も使われています。主流はXSDで、XML自身で書かれ、データ型の表現力が高く、読みこなすのに技術が要るほど冗長です。RelaxNGは書き心地がはるかに良い代わりに対応範囲が狭く、いつもどおりのトレードオフになっています。
スキーマがもたらすのは、コードがデータを見る前に境界で機械的に検査できる契約です。必須要素が存在するか、数値であるべき場所が数値か、列挙値が定められた集合に収まっているか。取引先との連携では、双方が合意している仕様がスキーマだけ、ということも珍しくありません。
パイプラインでの検証はコマンド1つで済みます。xmllint --schema schema.xsd file.xml --noout を実行すれば、エラーメッセージが行番号と要素を指し示してくれます。アプリケーションコードの中ではなく境界で検証する理由は、スキーマ違反なら理由付きのきれいな拒否になるのに対し、同じ問題が3層奥で見つかるとスタックトレースになるからです。
XPathでデータを取り出す
XPathはXMLのためのクエリ言語で、誰にとっても1時間を費やす価値があります。//book/title は文書内のどこにあっても book の下の title 要素をすべて選択します。//book[@id='b1'] は属性で選択し、//book[price>30]/title は子要素の値で絞り込みます。XSLT、JavaのXMLツール、Pythonのlxml、そして多くのブラウザ自動化ツールの土台にあるアドレス指定言語です。
面倒になるのは名前空間です。式の中で使うプレフィックスは自分で束縛しなければならず、デフォルト名前空間を持つ文書ではプレフィックスが付いていないように見えても要素はその名前空間に属しています。//book が何にも一致せず、SOAPレスポンスに対する最初のXPathが誰でも空で返ってくるのはこのためです。束縛を登録する方法はツールによってまちまちで、local-name() 関数が乱暴な逃げ道になります。
その上に構築された変換言語がXSLTで、あるXML語彙を別の語彙へ、あるいはHTMLへ変換する手段としては、手続き的なコードを書かずに済む最も実用的な方法であり続けています。難しいという評判は構文よりも関数型のスタイルに由来するものです。
XMLは今もいたるところにある
オフィス文書はXMLです。.docx や .xlsx はXMLパーツのディレクトリを収めたzipアーカイブで、壊れた文書を展開してファイルを1つ直せば復旧できることがあるのはそのためです。SVGもXMLなので、アイコンをテキストエディタで編集したりテンプレートで生成したりできます。RSSやAtomのフィードもXMLですし、このサイトが公開しているサイトマップもXMLです。
企業や行政のシステム連携では、SOAPは決して死んでいません。銀行、保険、HL7を介した医療、そして多くの国の税関・税務システムが、スキーマ付きの、しばしば署名されたXMLの上で動いています。XML SignatureとXML Encryptionを使えば、大きな文書の中の単一の要素だけに署名できます。これに相当する標準はJSONにはありません。
Androidのレイアウト、Mavenのビルド、.NETの設定、そして数え切れないCIの記述もXMLです。この形式は時代遅れというより流行遅れなのであって、無理なく読みこなせる力は長く使えるスキルです。
自分が書いたのではないXMLを解析する
多くのXMLライブラリの既定設定は、信頼できない入力に対して安全ではありません。原因は実体(エンティティ)の仕組みです。外部実体を使うと文書からローカルファイルやURLを参照できるため、アップロードされたXMLを既定設定で解析するサーバーは /etc/passwd を読み出して返すよう仕向けられかねません。これがXXE攻撃で、整理されてから何年も経った今も本番システムで見つかります。
もう一方の側面が展開です。billion laughs攻撃は実体を入れ子にして、数キロバイトの入力をメモリ上でギガバイト規模に膨らませ、プロセスを落とします。どちらの攻撃も特別なものは必要としません。仕様が定める機能を使った、ごく普通のXMLです。
防御は多くのライブラリでは1行で済みます。DTDの処理と外部実体の解決を無効にするだけです。既定値は言語やバージョンによって異なるので、そうなっているはずと決めつけず確認する価値があります。このページは外部のものを一切解決しません。文書をテキストとして読むだけで、参照先を取りに行くことはありません。
よくある質問
- 一部のXMLが1行のままなのはなぜですか?
- 「Hello <b>there</b>.」のように、テキストと要素が混在する内容(混合内容)だからです。ここにインデントを入れると改行と空白がテキストそのものに挿入され、表示すると「Hello there .」のように余計な隙間ができてしまいます。そのため該当する要素は書かれたまま保持します。多くのXML整形ツールはこれを構わず折り返し、ドキュメントを静かに書き換えてしまいます。
- 整形によってドキュメントの意味が変わることはありますか?
- ありません。それが上のルールの目的です。要素と要素の間の空白はレイアウトなので置き換え、混合内容の中の空白はデータなので保持します。属性の順序と引用符のスタイルは書かれたとおりに保ち、コメントとCDATAはそのままコピーし、元になかったXML宣言を追加することもありません。
- 「整形式」では具体的に何を確認しますか?
- すべての要素が対応するタグで閉じられていること、属性値が引用符で囲まれていること、コメントとCDATAブロックが終了していること、そしてルート要素がちょうど1つで、その外に余分なテキストがないことです。DTDやスキーマによる妥当性検証は行いません。それにはスキーマ自体が必要で、別のツールの領分です。
- 属性が拒否されたのはなぜですか?
- XMLではすべての属性値を引用符で囲む必要があります。HTMLならdisabled=trueやwidth=100と書いてもブラウザは解釈しますが、XMLでは許されず、後段のパーサーはすべて拒否します。HTML由来のドキュメントであれば、たいていここが最初に直すべき箇所です。
- 「圧縮」と「整形」の違いは何ですか?
- 同じドキュメントに対する逆方向の操作です。整形は読めるように要素間へ改行とインデントを挿入し、圧縮はそれをすべて取り除いて転送用に最小化します。どちらも混合内容には手を触れないため、一方を実行してからもう一方を実行すると元の状態に戻ります。
- XMLはどこかにアップロードされますか?
- いいえ。解析も整形もブラウザ内で行われるため、設定ファイルやSOAPのペイロード、エクスポートしたダンプなど、内部ホスト名や識別子、個人情報を含みがちなデータが端末を離れることはありません。
最終更新:
操作手順、期待した結果、実際の結果を記載してください。秘密情報や個人情報は削除してください。
