2009-10-29 18 views
9

XDMLドキュメントをXDocumentに読み込もうとしていますが、私にスローされた「宣言されていないエンティティへの参照」例外が発生しています。私は®»のようなエンティティを解決する必要があります。私は私の文書が適切に形成されていると信じてXDocumentに読み込むときにエンティティを解決するにはどうすればよいですか?

は、ここに頭である:私はスローされたこれらの例外を取得していたときに

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 

私はXDocument.Load(<StringReader>)を行うことです。

答えて

10

これはmsdnとブログ投稿のコラボレーションです。

 XDocument document; 

     using (var stringReader = new StringReader(output)) 
     { 
      var settings = new XmlReaderSettings 
      { 
       ProhibitDtd = false, 
       XmlResolver = new LocalXhtmlXmlResolver(bool.Parse(ConfigurationManager.AppSettings["CacheDTDs"])) 
      }; 

      document = XDocument.Load(XmlReader.Create(stringReader, settings)); 
     } 

    private class LocalXhtmlXmlResolver : XmlUrlResolver 
    { 
     private static readonly Dictionary<string, Uri> KnownUris = new Dictionary<string, Uri> 
     { 
      { "-//W3C//DTD XHTML 1.0 Strict//EN", new Uri("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd") }, 
      { "-//W3C XHTML 1.0 Transitional//EN", new Uri("http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd") }, 
      { "-//W3C//DTD XHTML 1.0 Transitional//EN", new Uri("http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd") }, 
      { "-//W3C XHTML 1.0 Frameset//EN", new Uri("http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd") }, 
      { "-//W3C//DTD XHTML 1.1//EN", new Uri("http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd") } 
     }; 

     private bool enableHttpCaching; 
     private ICredentials credentials; 

     public LocalXhtmlXmlResolver(bool enableHttpCaching) 
     { 
      this.enableHttpCaching = enableHttpCaching; 
     } 

     public override Uri ResolveUri(Uri baseUri, string relativeUri) 
     { 
      Debug.WriteLineIf(!KnownUris.ContainsKey(relativeUri), "Could not find: " + relativeUri); 

      return KnownUris.ContainsKey(relativeUri) ? KnownUris[relativeUri] : base.ResolveUri(baseUri, relativeUri); 
     } 

     public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) 
     { 
      if (absoluteUri == null) 
      { 
       throw new ArgumentNullException("absoluteUri"); 
      } 

      //resolve resources from cache (if possible) 
      if (absoluteUri.Scheme == "http" && this.enableHttpCaching && (ofObjectToReturn == null || ofObjectToReturn == typeof(Stream))) 
      { 
       var request = WebRequest.Create(absoluteUri); 

       request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Default); 

       if (this.credentials != null) 
       { 
        request.Credentials = this.credentials; 
       } 

       var response = request.GetResponse(); 

       return response.GetResponseStream(); 
      } 

      //otherwise use the default behavior of the XmlUrlResolver class (resolve resources from source) 
      return base.GetEntity(absoluteUri, role, ofObjectToReturn); 
     } 
    } 
+8

は一般的に悪い考えです - 離れてあなたが不必要にW3Cのサーバーをヒットしているという事実から、要求があれば、これはかなり遅く、インターネット接続が利用可能で信頼できるものである必要があります。もっと良いアプローチは、それらのDTDのローカルコピーをリソースとして保存し、それらを 'Assembly.GetManifestResourceStream'経由でロードすることです。または実行可能ファイルと同じディレクトリにあるローカルファイルとして保存されます。 –

+0

@Pavel洞察力に感謝します! – Dave

8

私はDaveと同じ問題を抱えていて、この質問に出会って私を助けました。 Daveの答えと最適化のためのPavelの提案に基づいて、クラスを更新しました。これで、DTDを埋め込みリソースとして格納し、必要に応じてロードできます。私はこのポストが数年前だと知っていますが、これは誰かを助けることができるかもしれません。

使用例:

XmlReaderSettings readerSettings = new XmlReaderSettings 
    { 
     DtdProcessing = DtdProcessing.Parse, 
     XmlResolver = new LocalXhtmlXmlResolver() 
    }; 

using (XmlReader reader = XmlReader.Create(xhtmlStream, readerSettings)) 
{ 
    XDocument xhtml = XDocument.Load(reader); 
    ... 
} 

LocalXhtmlXmlResolverクラス:WebからのDTDを解決

public class LocalXhtmlXmlResolver : XmlUrlResolver 
{ 
    private const string ResourcePrefix = "Your.Namespace.Here."; 

    private static readonly Dictionary<string, string> _knownDtds = new Dictionary<string, string> 
     { 
      { "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", ResourcePrefix + "xhtml1-strict.dtd" }, 
      { "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd", ResourcePrefix + "xhtml1-transitional.dtd" }, 
      { "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd", ResourcePrefix + "xhtml1-frameset.dtd" }, 
      { "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd", ResourcePrefix + "xhtml11.dtd" }, 
      { "http://www.w3.org/TR/xhtml1/DTD/-//W3C//ENTITIES Latin 1 for XHTML//EN", ResourcePrefix + "xhtml-lat1.ent" }, 
      { "http://www.w3.org/TR/xhtml1/DTD/-//W3C//ENTITIES Special for XHTML//EN", ResourcePrefix + "xhtml-special.ent" }, 
      { "http://www.w3.org/TR/xhtml1/DTD/-//W3C//ENTITIES Symbols for XHTML//EN", ResourcePrefix + "xhtml-symbol.ent" } 
     }; 

    private static readonly Dictionary<string, Uri> _knownUris = new Dictionary<string, Uri> 
     { 
      { "-//W3C//DTD XHTML 1.0 Strict//EN", new Uri("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd") }, 
      { "-//W3C XHTML 1.0 Transitional//EN", new Uri("http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd") }, 
      { "-//W3C//DTD XHTML 1.0 Transitional//EN", new Uri("http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd") }, 
      { "-//W3C XHTML 1.0 Frameset//EN", new Uri("http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd") }, 
      { "-//W3C//DTD XHTML 1.1//EN", new Uri("http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd") } 
     }; 

    public override Uri ResolveUri(Uri baseUri, string relativeUri) 
    { 
     return _knownUris.ContainsKey(relativeUri) ? _knownUris[relativeUri] : base.ResolveUri(baseUri, relativeUri); 
    } 

    public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) 
    { 
     if (absoluteUri == null) 
     { 
      throw new ArgumentNullException("absoluteUri"); 
     } 

     if (_knownDtds.ContainsKey(absoluteUri.OriginalString)) 
     { 
      string resourceName = _knownDtds[absoluteUri.OriginalString]; 
      Assembly assembly = Assembly.GetAssembly(typeof(LocalXhtmlXmlResolver)); 
      return assembly.GetManifestResourceStream(resourceName); 
     } 

     return base.GetEntity(absoluteUri, role, ofObjectToReturn); 
    } 
} 
関連する問題