2003年10月05日
川俣晶の縁側ソフトウェア技術雑記 total 4277 count

.NET FrameworkのXML DOMで、Saveメソッドに指定したファイル名をLoadメソッドに指定できない罠

Written By: 川俣 晶連絡先

 まず、クイズを出しましょう。

 .NET Framework 1.1上で動作するC#のサンプルソースです。

 これを実行した場合、どのような結果になるか、当ててみてください。

 C#と.NET Framework 1.1上のXML DOMの知識がある人が対象です。

using System;
using System.Xml;

class DomLoadSave
{
    [STAThread]
    static void Main(string[] args)
    {
        const string filename = @"c:\%AB.xml";
        const string src = "<a/>";
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(src);
        doc.Save(filename);

        XmlDocument doc2 = new XmlDocument();
        doc2.Load(filename);
        Console.WriteLine(doc2.OuterXml);
    }
}

 分かりましたか?

 結果は、以下のような例外です。

'System.IO.FileNotFoundException' のハンドルされていない例外が system.xml.dll で発生しました。

追加情報 : ファイル "c:\«.xml" が見つかりませんでした。

 なぜ、このような結果になるのか、理由が分かりますか?

 これはバグではありません。

 stringを引数に取るLoadメソッドとSaveメソッドは、引数の扱いが異なるというのが、理由です。よく説明を読むと、LoadメソッドはURLだと書いてありますが、Saveメソッドはそう書いてありません。つまり、Loadメソッドは引数をURLとして解釈する際に、ファイルとしてのアクセスも許しているだけの話であって、Windowsのファイル名記述のルールに厳密に合致しているわけではないのです。実際、%記号があると、それはURLエンコードと見なされて解釈されます。これにより、%記号を含む文字列をファイル名として与えると、LoadメソッドとSaveメソッドは異なる対象にアクセスしに行きます。

 これを回避するには、Loadメソッドにファイル名を文字列として渡すのではなく、ファイルをストリームとして開いてからそれを渡すようにすると良いでしょう。

 というバグ取りを現在作業中であったりします…… (涙) うっかり間違えやすいのよね、これ。

追記: DOMだけでなく、XmlReader/Writer系のクラスにも同じ罠があるらしい……。