====== C#でローカルのDTDファイルを使うXMLリゾルバを作る ====== 以前、[[2016-02-24|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(); DTDFileForURI = new Dictionary(); DTDStreamForURI = new Dictionary(); 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 URIForFPI { get; set; } Dictionary DTDFileForURI { get; set; } Dictionary DTDStreamForURI{ get; set; } } } } DTDファイルのパスを適宜変更し、''xmlDocument.Resolver = new LocalXmlResolver();''ってな感じで設定してやればおk。DTDを増やしたいときは''AddDTD''を増やす。DTDが内部で参照しているファイル(上記コードで言えば''xhtml-lat1.ent''とか)も漏れなく追加する必要がある。 一応、コードのライセンスはパブリックドメインってことで。煮るなり焼くなりお好きにどうぞ。