SheepDev

データ形式・構造化データ

XMLが読めない。閉じタグと文字参照をどう直す?

使うツール

外部システムから届いたXMLを読み込むと、最後の行でエラーになる。最後のタグを足しても、今度は別の場所で止まる。そんなときは、大きなファイルを何度も修正するより、一つのレコードまで小さくして、要素がどこで始まり、どこで終わるのかを見直す方が原因をつかみやすくなります。エラーが見つかった位置は、必ずしも間違いを書いた位置ではありません。

ここでは架空の注文データをXML整形・検証へ入力します。整形結果をアプリへ戻す前に、注文番号、名前、金額が意図どおり残っているかを確かめるのが目的です。実際の顧客名や認証情報は例に必要ありません。見やすい字下げができたことを、注文内容や連携先の仕様まで正しいという意味にはしないでください。

閉じタグを足す前に、名前を合わせる

対応しない閉じタグの例
<order id="A-17"><name>Notebook</title><amount>1200</amount></order>

この例ではnameで始めた要素をtitleで閉じています。XML整形・検証で実行し、成功した整形結果にならないことを確認してください。まず直す候補は末尾のorderではなく、名前が食い違った閉じタグです。本文にNotebookと書かれていることからtitleが正しいと推測するのではなく、連携先で使う項目名がnameなのかtitleなのかを確かめて、両側を同じ名前にします。

nameへ統一した入力
<order id="A-17"><name>Notebook</name><amount>1200</amount></order>

修正後はorderの内側へnameとamountが並ぶことを見ます。自動整形は、壊れた項目の意味を決めて修復する機能ではありません。エラーをなくすためだけに未知のタグを削除すると、構文は通っても必要な情報が消えます。一箇所を直して再実行する手順にすれば、どの変更が何を解決したかを説明しやすくなります。

タグの数が合っていても、入れ子は壊れる

交差した入れ子
<order><item><name>Notebook</item></name></order>

order、item、nameはそれぞれ一組ずつあります。それでも、最後に開いたnameより先にitemを閉じているため、正しい階層になりません。机に箱を重ねるときと同じように、内側を閉じてから外側を閉じます。この例なら閉じる順はname、item、orderです。タグを数えるだけの確認では、この種類の間違いを見逃します。

実ファイルで同じ問題を探すなら、一つ前の正常なレコードと、失敗するレコードを比べます。商品が一つのときは通るが二つだと失敗するなら、繰り返し部分の開始と終了が候補になります。ただし複数レコードを小さく切り出す際にも、外側のルート要素を残してください。切り出しによって作ったエラーを元の問題と取り違えないためです。

商品名の&を、タグの記号と混ぜない

特殊文字を含む正常な例
<order><name>Pen &amp; Paper</name><note>count &lt; 10</note></order>

表示したい商品名がPen & Paperなら、XMLの本文では&amp;という文字参照で&を表します。同様に、条件の説明として書きたい『count < 10』の小なり記号は&lt;にします。ここで直しているのは商品の名前そのものではなく、XMLという形式へ入れるための表記です。受け取り側で文字参照を解釈した後の値と、XMLソース上の文字列を区別してください。

文書全体の&を機械的に&amp;へ置換すると、すでに正しい&amp;まで&amp;amp;になり、受け取り側には文字としての『&amp;』が届くことがあります。元の文字列をXMLへ変換する処理と、完成したXMLをもう一度変換する処理を混ぜないことが大切です。整形結果でも文字参照が残るのは異常とは限りません。このツールは値をHTMLとして表示する画面ではありません。

属性の引用符も、値の境界になる

属性内の引用符を表す例
<item code="A-17" note="Use &quot;small&quot; size"><name>Notebook</name></item>

noteの値は二重引用符で囲んでいるため、その中の引用符は&quot;で表します。値を囲む記号を途中で終わらせると、後ろの文字列が別の属性のように読まれてしまいます。値が単語一つのときは気付かなくても、空白や引用符を含む名前が届いたときに失敗することがあります。境界を試す例として、空白、&、引用符を一つずつ加えた架空の名前を残しておくと再確認しやすくなります。

この整形ツールは内部パーサーの対応範囲で構文を確認しており、XML規格の全条件を厳密に証明する認証器ではありません。特にHTMLで見かける値なしの属性をXMLでも使えると考えないでください。ツールで処理できる入力であっても、連携先のXMLパーサーが受け入れるとは限りません。最終的には連携先が公開する入力仕様と、その環境での読込確認が必要です。

金額が空でも、タグは正しく閉じられる

amountを1200から空の文字列へ変えてみます。開始と終了が対応していれば、字下げされたXMLとして出力できる場合があります。しかし、注文の金額が必須か、数値であるべきか、通貨が何かは、タグの対応だけでは決まりません。amountをtwelveへ変えた場合も同じです。構文確認と、業務上の必須項目や型の検証を別の段階に分けると、何が確認済みなのかが明確になります。

この画面ではXSDに照らした注文書の検証や、連携先への送信試験は行いません。外部実体やDTDの宣言も読み込まず拒否します。DOCTYPE付きの文書が止まった場合、単に宣言を消せば元と同じ意味になるとは限りません。別の安全な検証環境で、その文書が何に依存しているかを確認してください。エラーを隠すことと、依存関係を解消することは違います。

修正を渡すときは、値と構造の両方を残す

直したXMLを渡すときは、『整形できた』だけでなく、どのタグがどのタグへ対応し、どの文字をエスケープしたかを説明します。例の注文ならA-17、Notebook、1200が残ることを確認し、名前に&を含む別の入力でも同じ確認をします。字下げによる空白の変更が意味を持つ混在内容や、署名済みXMLをそのまま再整形して使う用途には、この確認だけでは足りません。

最後に、正常例だけでなく、最初に失敗した小さな例もテスト用として残します。名前の不一致、交差した入れ子、引用符を含む属性を別々に試せれば、次に出力元の処理が変わったときも原因を探しやすくなります。大きな本番文書を共有するより、同じ失敗を起こす架空の最小例を用意する方が、問題の説明と情報の保護を両立できます。

参考資料

使うツール

使い方・解説 →