以前、C#でXmlDocumentを作る時にリゾルバでタイムアウトすると書いたが、ようやくローカルのDTDファイルを使ったXMLリゾルバが作れたので、コードをまるっと公開。尚、.NET Framework 4では殆ど同じことを行うXmlPreloadedResolver
クラスが追加されているので、使えるならそっちを使うのが良い。悲しいかな、うちは.NET 3.5なのさ……
using System; using System.Collections.Generic; using System.Xml; using System.IO; namespace ProductionKusoGA { class LocalXmlResolver : XmlResolver { public LocalXmlResolver() { } public override System.Net.ICredentials Credentials { set { } } public override Uri ResolveUri(Uri baseUri, string relativeUri) { Uri uri = DocTypeManager.Instance.GetDTDURI(relativeUri); return uri != null ? uri : base.ResolveUri(baseUri, relativeUri); } public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) { object entity = DocTypeManager.Instance.GetDTDStream(absoluteUri.AbsoluteUri); if (entity == null) { XmlUrlResolver resolver = new XmlUrlResolver(); entity = resolver.GetEntity(absoluteUri, role, ofObjectToReturn); } return entity; } class DocTypeManager { public static readonly DocTypeManager Instance = new DocTypeManager(); public Uri GetDTDURI(string inFPI) { Uri dtdURI = null; URIForFPI.TryGetValue(inFPI, out dtdURI); return dtdURI; } public FileStream GetDTDStream(string inURI) { FileStream stream = null; if (DTDStreamForURI.TryGetValue(inURI, out stream) == false) { string dtdFile = null; if (DTDFileForURI.TryGetValue(inURI, out dtdFile)) { string RESOURCE_DIR = "..."; stream = new FileStream(Path.Combine(RESOURCE_DIR, dtdFile), FileMode.Open); DTDStreamForURI.Add(inURI, stream); } } return stream; } DocTypeManager() { URIForFPI = new Dictionary<string,Uri>(); DTDFileForURI = new Dictionary<string,string>(); DTDStreamForURI = new Dictionary<string, FileStream>(); AddDTD("-//W3C//DTD XHTML 1.0 Strict//EN", @"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", @"path/to/xhtml1-strict.dtd"); AddDTD("-//W3C//DTD XHTML 1.0 Trasitional//EN", @"http://www.w3.org/TR/xhtml1/DTD/xhtml1-trasitional.dtd", @"path/to/xhtml1-trasitional.dtd"); AddDTD("-//W3C//DTD XHTML 1.0 Frameset//EN", @"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd", @"path/to/xhtml1-frameset.dtd"); AddDTD("xhtml-lat1.ent", @"http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent", @"path/to/xhtml-lat1.ent"); AddDTD("xhtml-symbol.ent", @"http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent", @"path/to/xhtml-symbol.ent"); AddDTD("xhtml-special.ent", @"http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent", @"path/to/xhtml-special.ent"); } ~DocTypeManager() { foreach (var pair in DTDStreamForURI) { if (pair.Value != null) { pair.Value.Dispose(); } } } void AddDTD(string inFPI, string inURI, string inFilepath) { URIForFPI.Add(inFPI, new Uri(inURI)); DTDFileForURI.Add(inURI, inFilepath); } Dictionary<string, Uri> URIForFPI { get; set; } Dictionary<string, string> DTDFileForURI { get; set; } Dictionary<string, FileStream> DTDStreamForURI{ get; set; } } } }
DTDファイルのパスを適宜変更し、xmlDocument.Resolver = new LocalXmlResolver();
ってな感じで設定してやればおk。DTDを増やしたいときはAddDTD
を増やす。DTDが内部で参照しているファイル(上記コードで言えばxhtml-lat1.ent
とか)も漏れなく追加する必要がある。
一応、コードのライセンスはパブリックドメインってことで。煮るなり焼くなりお好きにどうぞ。