XML データのエンコード
XML データのエンコード
Chris Lovett
Microsoft Corporation
March 2000
日本語版最終更新日 2000 年 8 月 2 日
要約 : この記事では、文字エンコーディングの仕組みと、それが特に XML と MSXML DOM において、どのように機能するかについて説明します。
最近、どうすれば XML ファイルを用いて異種プラットフォーム間のデータ転送ができるようになるのか、という質問を多くの人から受けます。彼らは XML 文書を作成してデータを入力し、そのデータに付加したタグの形式を整え、さらに <?xml version="1.0"?>
宣言まで付けています。ところがそのファイルをアップロードしようとすると、データに問題があるという思いもよらないエラー メッセージが Microsoft® XML パーサー (MSXML) から返されます。XML の初心者は、釈然としないでしょう。これでうまく行くはずではないかと。
実はそうでもないのです。MSXML から予期せぬエラー メッセージを受け取った場合、データを受け取るプラットフォームと送信元のプラットフォームとでデータの格納方法が異なるために、文字エンコーディングの問題が発生したと考えられます。
クロスプラットフォームのデータ フォーマット
2 つの異なるコンピュータの接続に成功して以来、コンピュータのソフトウェア業界とハードウェア業界は、クロスプラットフォーム技術を開発し、異種プラットフォーム間でデータを共有するために奮闘を続けてきました。その初期の頃以降、コンピュータの種類や接続方法、またそれらの間で共有するデータの種類は爆発的に増加し、状況はますます複雑化しています。
クロスプラットフォームのプログラミング技術に関する長年の研究の末に、現在では (そしておそらく今後も) シンプルな "標準データ フォーマット" がクロスプラットフォーム ソリューションを実現する唯一の手段となっています。Web の成功は、まさにこれらのフォーマットが基盤となりました。Web サーバーと Web ブラウザの間で現在主にやり取りされているのは、HTTP ヘッダーと HTML ページで、どちらも標準のテキスト フォーマットです。
以下のセクションでは、文字エンコーディングと標準文字セット、Unicode、HTML Content-Type ヘッダー、HTML Content-Type メタタグ、および文字エンティティについて説明します。これらの概念をすでに理解している場合は、 XML Document Object Model (DOM) プログラマのための XML データのエンコードに関するヒントに進んでください。詳細については、「 XML と文字エンコーディング」を参照してください。
文字エンコーディングについて
標準テキスト フォーマットは、標準文字セットに基づいています。ご存じのように、コンピュータではテキストが数字として格納されます。ところがシステムが異なると、同じテキストが異なる数字で格納される場合があります。次の表は、ある範囲のバイトがどのように格納されるかを示したものです。最初に示すのは Microsoft Windows® が稼働する一般的なコンピュータで、既定のコード ページ 1252 を使用しています。次に示すのは、一般的な Apple® Macintosh® コンピュータで、Macintosh Roman コード ページを使用しています。
バイト | Windows | Macintosh |
140 | Œ | å |
229 | å | Â |
231 | Ç | Á |
232 | è | Ë |
233 | é | È |
たとえば http://www.barnesandnoble.com/ で本を注文する場合、大部分の人は、自分が使用する Macintosh コンピュータと www.barnesandnoble.com を構成している Windows 2000 Web サーバーで、文字の格納方法が異なることを知りません。インターネット注文書の納入先の欄にスウェーデンの自宅住所を入力するときも、文字 "å" (Macintosh ではバイト値 140) がインターネットで正しく配信されるものと信じ、自分のメッセージを受け取って処理するコンピュータによりバイト値 140 が文字 "Œ" に変換されることはまったく知りません。
Unicode
Unicode コンソーシアム (英語) では、世界中の言語を含む共通のコード ページ (1 つの文字に 1 バイトではなく 2 バイトを使用) を定義して、異なるコード ページ間のマッピングに関する問題を解消しようと考えました。
では、Unicode がクロスプラットフォームの文字エンコーディングに関する問題を解決するのなら、なぜ Unicode が唯一の標準にならなかったのでしょう。第 1 の問題は、Unicode に切り替えると、場合によってファイル サイズが 2 倍になることです。これはネットワーク中心の世界では好ましくありません。このため、いまだに ISO-8859-1 ~ ISO-8859-15、シフト JIS、EUC-KR などの文字セットを使用している人もいます。
第 2 の問題は、Unicode ベースではないシステムが世の中にまだ多数存在することです。つまりネットワーク上で、Unicode 文字を構成するバイト値の一部が原因で、これらの旧システムに大きな問題が起きる可能性があります。そのために定義されたのが Unicode 変換フォーマット (UTF) です。UTF では、ビット シフト技法を使って Unicode 文字をバイト値にエンコードします。旧システム上では、これらのバイト値が "透過的" になり、結果として問題なく流れます。
文字エンコーディングで最も普及しているのは UTF-8 です。UTF-8 は、標準 Unicode の最初の 127 文字 (これは偶然にもラテン語の A ~ Z、a ~ z、0 ~ 9、および数個の句読文字にあたる) を、1 バイトの値に直接マッピングします。次に、そのバイトの上位ビットを使用するビット シフト技法によって、残りの Unicode 文字をエンコードします。その結果、スウェーデン語の小文字 " å " (0xE5) は、" Ã¥ " (0xC3 0xA5) という意味不明の 2 バイト値となります。したがって、頭の中でビット シフトを実行できる人でない限り、UTF-8 でエンコードされたデータを人間が読むことはできません。
Content-Type ヘッダー
旧来からの文字セットが依然として使用されているため、データの文字セットも指定しなければデータ転送の問題は解消されません。これを受けて、インターネット電子メールおよび HTTP プロトコルの関連団体では、メッセージ ヘッダーの " Content-Type " プロパティに文字セットを指定する標準の方法を規定しました。このプロパティでは、Internet Assigned Numbers Authority (IANA) (英語) によって定義された登録文字セット名の一覧から文字セットを指定します。一般的な HTTP ヘッダーには次のようなテキストが含まれています。
HTTP/1.1 200 OK
Content-Length: 15327
Content-Type: text/html; charset:ISO-8859-1;
Server: Microsoft-IIS/5.0
Content-Location: https://www.microsoft.com/Default.htm
Date: Wed, 08 Dec 1999 00:55:26 GMT
Last-Modified: Mon, 06 Dec 1999 22:56:30 GMT
このヘッダーは、ヘッダーの後に続く内容に ISO-8859-1 文字セットが使用されていることをアプリケーションに通知しています。
Content-Type メタタグ
" Content-Type " プロパティは省略可能であり、アプリケーションによっては HTTP ヘッダー情報が取り除かれて HTML だけが渡されることもあります。これに対処するため、HTML 標準の団体では、使用する HTML 文書の文字セットがわかるように、HTML 文書内部に文字セットを指定する方法としてオプションのメタタグを規定しました。
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
この場合、文字セット ISO-8859-1 を指定することで、この特定の HTML ページではバイト値 229 が " å " を意味することを宣言しています。これでこのページは、どのシステムでも完全にあいまい性がなくなり、データが誤って解釈されることもありません。残念ながらこのメタタグはオプションであるため、エラーの発生する可能性がまだ残っています。.
文字エンティティ
登録されているすべての文字セットを、すべてのシステムがサポートしているわけではありません。たとえば、IBM 社のメインフレームの文字セットである EBCDIC を多くのプラットフォームがサポートしているとは思えません。Windows NT ではサポートしていますが、ほかの多くのプラットフォームではサポートしていないでしょう。 http://www.ibm.com のホーム ページに ASCII が使用されているのもおそらくそういう理由からでしょう。
HTML では、バックアップ プランとして、ページ内の個々の文字を、対応する Unicode 文字値を指定することでエンコードできるようになっています。これらの文字エンティティは文字セットとは関係なく解析され、それぞれの Unicode 値が特定されます。このための構文は、"å" あるいは "å"です。
XML と文字エンコーディング
XML では、HTML から上記のアイデアを採り入れ、それをさらに推し進めて、使用されている文字セット エンコーディングを特定する確実なアルゴリズムが定義されています。XML では、XML 宣言のオプションのエンコーディング属性で、文字エンコーディングを定義します。次のアルゴリズムで、既定のエンコーディングが特定されます。
ファイルの始まりが、バイト順を表す Unicode の記号 [0xFF 0xFE] または [0xFE 0xFF] である場合、その文書には UTF-16 エンコーディングが使用されていると見なされます。そうでない場合、そのファイルには UTF-8 が使用されています。
次に示すのは、いずれも正しい同等の XML 文書です。
文字セットまたはエンコーディング | HTTP ヘッダー | XML 文書 |
ISO-8859-1 | Content-Type: text/xml; charset:ISO-8859-1; | <test>å</test> |
UTF-8 | Content-Type: text/xml; | <test>Ã¥</test> |
ISO-8859-1 | Content-Type: text/xml; | <?xml version="1.0" encoding="ISO-8859-1"?> <test>å</test> |
UTF-8 (using character entities) | Content-Type: text/xml; | <test>å</test> |
UTF-16 (Unicode with byte-order mark) | Content-Type: text/xml; | ff fe 3c 00 74 00 65 00 73 00 74 00 3e 00 e5 00 ..<.t.e.s.t.>... 3c 00 2f 00 74 00 65 00 73 00 74 00 3e 00 0d 00 <./.t.e.s.t.>... 0a 00 |
文字セットと MSXML DOM
ここまでは、文字をエンコードするさまざまな方法について説明してきました。次に MSXML DOM の XML 文書を読み込む方法、およびエンコードがあいまいな文字を検出した場合に表示されるエラー メッセージの種類について説明します。XML DOM 文書を読み込む主なメソッドは、 LoadXML メソッドと Load メソッドです。
LoadXML メソッドでは、UCS-2 または UTF-16 でエンコードされた Unicode BSTR のみを処理します。有効な Unicode BSTR 以外を LoadXML に渡すと、このメソッドは読み込みに失敗します。
Load メソッドでは、VARIANT として以下を指定することができます。
値 | 説明 |
URL | VARIANT が BSTR である場合は、URL として解釈されます。 |
VT_ARRAY | VT_UI1 | VARIANT には、エンコードされた未処理のバイトを含んだ SAFEARRAY を指定することもできます。 |
IUnknown | VARIANT が IUnknown インターフェイスである場合、DOM 文書は IStream 、IPersistStream 、および IPersistStreamInit に対して QueryInterface を呼び出します。 |
Load メソッドは、次のアルゴリズムによって、XML 文書の文字エンコーディングまたは文字セットを特定します。
Content-Type HTTP ヘッダーに文字セットが定義されている場合、この文字セットは XML 文書内のどの記述よりも優先されます。SAFEARRAY と IStream の両メカニズムには HTTP ヘッダーが存在しないため、このアルゴリズムは適用されません。
2 バイトの Unicode バイト順記号がある場合、エンコーディングは UTF-16 と見なされます。UTF-16 では、ビッグ エンディアンとリトル エンディアンの両方を処理できます。
4 バイトの Unicode バイト順記号 (0xFF 0xFE 0xFF 0xFE) がある場合、エンコーディングは UTF-32 と見なされます。UTF-32 では、ビッグ エンディアンとリトル エンディアンの両方を処理できます。
上記以外の場合、エンコーディングは UTF-8 と見なされます。ただし、XML 宣言にほかの文字セット (ISO-8859-1、Windows-1252、シフト JIS など) を指定するエンコーディング属性が指定されている場合を除きます。
エンコーディングの問題に関して XML DOM から返されるエラーには 2 種類あります。その 1 つは通常、文書内の文字が XML 文書のエンコーディングと一致しないことを示します。
テキストの内容に無効な文字が見つかりました。
ParseError オブジェクトによって、不正な文字がどの行にあるかが示されるので、問題を修正することができます。
もう 1 つのエラーは、Unicode バイト順記号で始めた (または LoadXML メソッドを呼び出した) にもかかわらず、エンコーディング属性に 2 バイトのエンコーディング以外 (UTF-8 や Windows-1250 など) が指定されていることを示します。
現在のエンコードから指定したエンコードへの切り替えはサポートしていません。
一方、 Load メソッドを呼び出し、1 バイトのエンコーディング (バイト順記号なし) で始めているにもかかわらず、エンコーディング属性に 2 バイトまたは 4 バイトのエンコーディング (UTF-16 や UCS-4 など) を指定してしまう場合もあります。
結論として、UTF-8、シフト JIS、Windows-1250 などのマルチバイトの文字セットと、XML 宣言でエンコーディング属性を使用する UTF-16、UCS-2、UCS-4 などの Unicode 文字エンコーディングを切り替えることはできません。これは、宣言自体が使用する 1 文字あたりのバイト数が、残りの文書と同じでなければならないためです。
最後に、 IXMLHttpRequest インターフェイスでは次の方法でダウンロードされたデータを使用します。
メソッド | 説明 |
ResponseXML | Load メソッドと同じルールを使用して、応答エンティティ本体を MSXML DOM パーサーによって解析されたとおりに表します。 |
ResponseText | 応答エンティティ本体を文字列として表します。このメソッドは、受け取ったメッセージ本体をむやみに UTF-8 からデコードします。これは今後の MSXML Web リリースで修正しなければならない問題です。 |
ResponseBody | 応答エンティティ本体を、符号なしのバイトの配列として表します。 |
ResponseStream | 応答エンティティ本体を、 IStream インターフェイスとして表します。 |
MSXML で新しい XML 文書を作成する
一度読み込まれた XML 文書は、エンコーディングの問題を一切気にすることなく、DOM を使って操作することができます。これはその文書が Unicode としてメモリに格納されるためです。XML DOM のすべてのインターフェイスは、2 バイトの Unicode 文字列である COM BSTR に基づいています。つまり、あらゆる Unicode 文字を含む MSXML DOM 文書を、メモリ内で最初から構築することができます。メモリ内のこの DOM 文書は、Unicode 文字の意味で混乱することもなく、すべてのコンポーネントで共有できます。ただし、この DOM 文書を保存する場合は、MSXML がすべてのデータを既定で UTF-8 にエンコードします。たとえば以下を実行するとします。
var xmldoc = new ActiveXObject("Microsoft.XMLDOM")
var e = xmldoc.createElement("test");
e.text = "・;
xmldoc.appendChild(e);
xmldoc.save("foo.xml");
これによって作成される UTF-8 でエンコードされたファイルでは、次のようになります。
<test>Ã¥</test>
注 : この例は、ブラウザ環境の外部でコードを実行する場合のみ機能します。ブラウザ内部で Save メソッドを呼び出すと、セキュリティ上の制限から同じ結果が得られません。
これはおかしいように思われますが、間違いではありません。次のテストでは、UTF-8 でエンコードされたファイルを読み込み、UTF-8 が元の Unicode 文字の値 229 にデコードされるかどうかを確かめます。
var xmldoc = new ActiveXObject("Microsoft.XMLDOM")
xmldoc.load("foo.xml");
if (xmldoc.documentElement.text.charCodeAt(0) == 229)
{
WScript.echo("正常に機能しました!!");
}
XML DOM の Save メソッドで使用されるエンコーディングを変更するには、次のように文書の最初にエンコーディング属性を指定した XML 宣言を記述する必要があります。
var pi = xmldoc.createProcessingInstruction("xml",
" version='1.0' encoding='ISO-8859-1'");
xmldoc.appendChild(pi);
save メソッドを呼び出すと、次のように ISO-8859-1 でエンコードされたファイルが得られます。
<?xml version="1.0" encoding="ISO-8859-1"?>
<test>å</test>
XML プロパティで混乱しないようにしてください。 XML プロパティは Unicode 文字列を返します。ISO-8859-1 エンコーディングを宣言した後に DOMDocument オブジェクトで XML プロパティを呼び出すと、次の Unicode 文字列が返されます。
<?xml version="1.0"?>
<test>å</test>
ISO-8859-1 エンコーディングの宣言が消えています。これは正常な動作です。宣言が消されたのは、この文字列で LoadXML を呼び出せるようにするためで、実際にそれでうまく機能します。この処理が行われないと、"現在のエンコードから指定したエンコードへの切り替えはサポートしていません。" というエラー メッセージが表示されて、LoadXML は失敗します。
結論
文字エンコーディングの仕組みと、それが特に XML と MSXML DOM でどのように行われるかが、この記事でおわかりいただければ幸いです。文字セットのエンコーディングは理解してしまえば簡単です。また、XML はエンコーディングに関して不明瞭さがない点で優れています。MSXML DOM には、気を付けなければならないクセがいくつかありますが、それでも XML エンコーディングの読み書きを可能にする強力なツールであることに変わりはありません。
詳細情報
「Character Encoding Model (英語) 」Ken Whistler、Mark Davis 共著
「IANA Character Sets (英語) 」
RFC の一覧については、Internet Engineering Task Force (IETF) のサイト ( http://www.ietf.org ) (英語) を参照してください。
「Microsoft Global Software Development: Compatibility Issues with Mixed Environments (英語) 」