TODO いつか書き直す。(.NET Coreにおける問題のIssueが解決し、.NET Core 1.2がリリースされたら記事を書き直す。ソースコードの予定場所
(現在の中身は 2014 年 6 月 4 日当時のコードを使ったもの)
(例外の画像)
1 2 3 4 5 6 7 8 9
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE math PUBLIC "-//W3C//DTD MathML 3.0//EN" "http://www.w3.org/Math/DTD/mathml3/mathml3.dtd"> <math xmlns="http://www.w3.org/1998/Math/MathML"> <mn>2</mn> <mo>×</mo> <mn>5</mn> <mo>=</mo> <mn>10</mn> </math>
|
上のような XML を読み込ませると「宣言されていないエンティティ’times’への参照です。」というエラーが出る場合の対処法など。
1: XmlReader を使って、Dtd の評価をオンにして乗り切る
一番簡単に思えるのは、 XmlReader
とその設定を用いた評価です。
DtdProcessing
に Parse
を指定することで、Dtd を考慮して読み込んでくれます。
速いわけではないので、必要ない人は 1 を飛ばしてください。
プログラム
1 2 3 4 5 6 7
| DateTime start = DateTime.Now; var doc = XDocument.Load(XmlReader.Create("MathMLFile.xml", new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse })); Console.WriteLine(DateTime.Now - start); Console.WriteLine(doc.ToString());
|
出力
1 2 3 4 5 6 7 8 9 10 11
| 00:00:08.3144756
<!DOCTYPE math PUBLIC "-//W3C//DTD MathML 3.0//EN" "http://www.w3.org/Math/DTD/mathml3/mathml3.dtd"[]> <math xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink"> <mn xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">2</mn> <mo xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">×</mo> <mn xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">5</mn> <mo xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">=</mo> <mn xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">10</mn> </math>
|
結果
出力の最初を見てください。個人のネットワーク環境に左右されるとはいえ、 8 秒の処理は長すぎます。原因は XML の宣言で外部(インターネット上)の定義ファイルを指定しており、評価時にダウンロードしてくる処理が(毎回)必要になるからです。
これを解決するためには、あらかじめローカルに ‘.dtd’ ファイルを保存しておくとよいです。( XML がモジュール化されている場合は、そのモジュールすべてが必要)
2: XmlReader を使って、自作の派生クラスでローカルファイルの定義を使わせる
少々面倒な処理が必要になりますが、 XML に使うフォーマットが分かっている場合は、こちらの方が速くて便利です。また、クラス化することで処理が分かれるので見やすくなる?
XmlReader.Create
の設定で、 XmlResolver
に自作の派生クラスのインスタンスを指定します。
プログラム
本処理1 2 3 4 5 6 7 8 9 10 11
| DateTime start = DateTime.Now; var doc = XDocument.Load(XmlReader.Create( new FileStream("MathMLFile.xml", FileMode.Open, FileAccess.Read, FileShare.Read), new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse, XmlResolver = new MathMLResolver() }));
Console.WriteLine(DateTime.Now - start); Console.WriteLine(doc.ToString());
|
自作の派生クラス1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class MathMLResolver : System.Xml.Resolvers.XmlPreloadedResolver { public MathMLResolver() : base() { this.Add( new Uri("http://www.w3.org/Math/DTD/mathml3/mathml3.dtd"), new FileStream("mathml3.dtd", FileMode.Open, FileAccess.Read, FileShare.Read)); this.Add( new Uri("http://www.w3.org/Math/DTD/mathml3/mathml3-qname.mod"), new FileStream("mathml3-qname.mod", FileMode.Open, FileAccess.Read, FileShare.Read)); foreach (var file in Directory.EnumerateFiles(Environment.CurrentDirectory, "*.ent") .Select(fullPath => Path.GetFileName(fullPath))) { this.Add( new Uri("http://www.w3.org/Math/DTD/mathml3/" + file), new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)); } } }
|
出力
1 2 3 4 5 6 7 8 9 10
| 00:00:00.1200068
<!DOCTYPE math PUBLIC "-//W3C//DTD MathML 3.0//EN" "http://www.w3.org/Math/DTD/mathml3/mathml3.dtd"[]> <math xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink"> <mn xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">2</mn> <mo xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">×</mo> <mn xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">5</mn> <mo xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">=</mo> <mn xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink">10</mn> </math>
|
結果
ローカルから読むことで速くなりました。
自作のクラスは、 XmlPreloadedResolver
クラスから派生しています。 XmlPreloadedResolver
クラスは、 XML 関係ファイルの URI と、それに対応するファイルの関係を決めることができ、今回はそれにローカルファイルを指定しています。
今回は MathML のものを用いました。関係するすべてのファイルを実行フォルダに保存し、読み込ませています。
(ここのサイトも追加で参考になりそう。 .NET Framework 4.5.2 でXmlReaderのDTD読み込みの挙動が変わっていた - ぽにょろん)