2009-10-29 15 views
9

Estoy tratando de cargar un documento XHTML en un XDocument pero obtengo excepciones de "referencia a entidades no declaradas". Necesito resolver entidades como ® y ».¿Cómo puedo resolver entidades al cargar en un XDocument?

Creo que mi documento está bien formado, aquí es la cabeza:

<?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"> 

Cuando hago un XDocument.Load(<StringReader>) es cuando me estoy haciendo estas excepciones producidas.

Respuesta

10

Esta es una colaboración de publicaciones msdn y blog.

 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

Resolución de DTD de la Web es generalmente una mala idea - aparte del hecho de que está golpeando innecesariamente servidores del W3C con las solicitudes, esto es bastante lento y depende de que la conexión a Internet esté disponible y sea confiable. Un enfoque mucho mejor es almacenar copias locales de esas DTD como recursos, y cargarlas a través de 'Assembly.GetManifestResourceStream'; o como archivos locales en el mismo directorio que su ejecutable. –

+0

@Pavel ¡gracias por la información! – Dave

8

Tuve el mismo problema que Dave y encontré esta pregunta que me ayudó mucho. Basado en la respuesta de Dave y la sugerencia de Pavel para la optimización, actualicé la clase. Ahora las DTD pueden almacenarse como recursos integrados y cargarse si es necesario. Sé que esta publicación tiene algunos años, pero quizás esto pueda ayudar a alguien. el uso

Ejemplo: Clase de

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

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

LocalXhtmlXmlResolver:

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); 
    } 
} 
Cuestiones relacionadas