XmlResolver не вызывается с помощью XslCompiledTransform

1

Я пытаюсь разрешить ссылки на функции документа в XslCompiledTransform, используя пользовательский XmlResolver, а не фактические файлы в файловой системе. Используя устаревший XslTransform, я могу заставить его работать нормально, но не с скомпилированным преобразованием (которое я хочу использовать по соображениям производительности).

Исходный XML-документ выглядит следующим образом:

<?xml version=""1.0"" encoding=""UTF-8""?>
<StackOverflow>
</StackOverflow>

XSLT выглядит так:

<xsl:stylesheet version=""1.0""
                xmlns:xsl=""http://www.w3.org/1999/XSL/Transform""
                xmlns:xs=""http://www.w3.org/2001/XMLSchema""
                exclude-result-prefixes=""xs"">
    <xsl:variable name=""author"" select=""document('data.xml')""/>

    <xsl:template match=""/"">
        <StackOverflow>
            <Author>
                <xsl:copy-of select=""$author/Person/*""/>
            </Author>
        </StackOverflow>
    </xsl:template>
</xsl:stylesheet>

Ожидаемый результат:

<?xml version="1.0" encoding="utf-16"?>
<StackOverflow>
 <Author>
  <FirstName>Aaron</FirstName>
  <LastName>Janes</LastName>
 </Author>
</StackOverflow>

Часть, которую я хочу использовать пользовательский XmlResolver for, - это "документ (" data.xml "), который я не хочу загружать из файла на диске, но для краткости и простоты в этом сообщении, из статического словаря в пределах resolver, вот так:

namespace transform
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Xml;

    public class XmlStaticResolver : XmlResolver
    {
        private const string PersonXml = 
@"<?xml version=""1.0"" encoding=""utf-8""?>
<Person>
    <FirstName>Aaron</FirstName>
    <LastName>Janes</LastName>
</Person>";

        private readonly IDictionary<string, string> _input;

        public XmlStaticResolver()
        {
            _input = new Dictionary<string, string>
            {
                { "data.xml", PersonXml }
            };
        }

        public override object GetEntity(Uri absoluteUri, 
                                         string role, 
                                         Type ofObjectToReturn)
        {
            var host = absoluteUri.Host;
            var xml = _input[host];
            var bytes = Encoding.UTF8.GetBytes(xml);
            var result = new MemoryStream(bytes);

            return result;
        }

        public override Uri ResolveUri(Uri baseUri, string relativeUri)
        {
            return new Uri("static://" + relativeUri);
        }
    }
}

Резольвер принимает относительный URI ("data.xml") и добавляет поддельный протокол, чтобы метод GetEntity рассматривал "data.xml" как хост URL-адреса. Затем значение просматривается в статическом словаре и возвращается как поток памяти для используемого преобразования.

Когда я использую этот метод с использованием устаревшего XmlTransform, он работает точно так, как ожидалось, например, вызывая метод Transform с вышеупомянутым XSLT, и XML получает ожидаемый результат:

namespace transform
{
    using System.IO;
    using System.Xml;
    using System.Xml.XPath;
    using System.Xml.Xsl;

    public class ObsoleteTransform
    {
        public string Transform(string xslt, string xml)
        {
            var resolver = new XmlStaticResolver();
            var transform = new XslTransform { XmlResolver = resolver };
            using (var transformReader = CreateXmlReader(new StringReader(xslt), resolver))
            {
                transform.Load(transformReader);
            }

            using (var writer = new StringWriter())
            {
                using (var xwriter = CreateXmlWriter(writer))
                {
                    using (var inputReader = new StringReader(xml))
                    {
                        var input = new XPathDocument(inputReader);
                        transform.Transform(input, 
                                            new XsltArgumentList(), 
                                            xwriter);
                    }
                }

                return writer.ToString();
            }
        }

        private static XmlReader CreateXmlReader(TextReader reader, 
                                                 XmlResolver resolver)
        {
            var settings = new XmlReaderSettings
            {
                XmlResolver = resolver
            };

            var xreader = XmlReader.Create(reader, settings, (string)null);
            return xreader;
        }

        private static XmlWriter CreateXmlWriter(TextWriter writer)
        {
            var settings = new XmlWriterSettings
            {
                Indent = true,
                IndentChars = " ",
                NewLineChars = "\r\n"
            };

            var xwriter = XmlWriter.Create(writer, settings);
            return xwriter;
        } 
    }
}

Однако, когда я пытаюсь использовать XslCompiledTransform, я получаю исключение:

System.Xml.Xsl.XslTransformException: 
    An error occurred while loading document 'data.xml'.

Код, который я использую, чтобы попытаться преобразовать с помощью XslCompiledTransform:

namespace transform
{
    using System.IO;
    using System.Xml;
    using System.Xml.XPath;
    using System.Xml.Xsl;

    public class CompiledTransform
    {
        public string Transform(string xslt, string xml)
        {
            var resolver = new XmlStaticResolver();
            var xsltSettings = new XsltSettings(true, true);
            var transform = new XslCompiledTransform();
            using (var transformReader = CreateXmlReader(new StringReader(xslt), resolver))
            {
                transform.Load(transformReader, xsltSettings, resolver);
            }

            using (var writer = new StringWriter())
            {
                using (var xwriter = CreateXmlWriter(writer))
                {
                    using (var inputReader = new StringReader(xml))
                    {
                        var input = new XPathDocument(inputReader);
                        transform.Transform(input, 
                                            new XsltArgumentList(), 
                                            xwriter);
                    }
                }

                return writer.ToString();
            }
        }

        private static XmlReader CreateXmlReader(TextReader reader, 
                                                 XmlResolver resolver)
        {
            var settings = new XmlReaderSettings
            {
                XmlResolver = resolver
            };

            var xreader = XmlReader.Create(reader, settings, (string)null);
            return xreader;
        }

        private static XmlWriter CreateXmlWriter(TextWriter writer)
        {
            var settings = new XmlWriterSettings
            {
                Indent = true,
                IndentChars = " ",
                NewLineChars = "\r\n"
            };

            var xwriter = XmlWriter.Create(writer, settings);
            return xwriter;
        } 
    }
}

Мне кажется, что, используя скомпилированное преобразование, резольвер не вызывается. Кто-нибудь знает, почему?

Теги:
xslt

1 ответ

0

Вы передаете свой XmlStaticResolver resolver как для XmlReader.Create() и для XslCompiledTransform.Load(). Вы возвращаете свой PersonXml/data.xml, то есть XML, в обоих случаях. Но XslCompiledTransform.Load() вызывает XmlStaticResolver.ResolveUri() с Uris для разрешения таблицы стилей, т.е. путей к файлам .xslt. Во всяком случае, я не вижу, что вы хотите передать свой XmlStaticResolver resolver в CreateXmlReader(new StringReader(xslt), resolver)) который читает XSL; Я не знаю, как это работает в старом коде....

Я думаю, вы обнаружите, что ваша ошибка System.Xml.Xsl.XslTransformException: An error occurred while loading document 'data.xml'. говорит вам, что он не пытается загрузить data.xml в качестве документа таблицы стилей, а не как XML-документ!

Вы говорите: "Мне кажется, что, используя скомпилированное преобразование, резольвер не вызывается", но при условии, что вы можете отлаживать только XmlStaticResolver.ResolveUri() останова XmlStaticResolver.ResolveUri() и видеть, где она вызывается и с какими аргументами.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню