Как разрешить объекты при загрузке в XDocument? - PullRequest
9 голосов
/ 29 октября 2009

Я пытаюсь загрузить документ XHTML в 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>), я получаю эти исключения.

Ответы [ 2 ]

10 голосов
/ 30 октября 2009

Это совместная работа сообщений 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 голосов
/ 16 июля 2013

У меня была та же проблема, что и у Дэйва, и я столкнулся с этим вопросом, который мне очень помог. Основываясь на ответе Дэйва и предложении Павла по оптимизации, я обновил класс. Теперь DTD могут храниться как встроенные ресурсы и загружаться при необходимости. Я знаю, что этому посту несколько лет, но, возможно, это может кому-то помочь.

Пример использования:

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

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

LocalXhtmlXmlResolver class:

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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...