Как разрешить включение XSL в преобразование, которое загружает XSL из строки? - PullRequest
5 голосов
/ 15 июня 2009

.NET 2.0 / VS2005

Я пытаюсь использовать класс XslCompiledTransform для выполнения XSL-преобразования. У меня есть два XSL-файла, первый из которых содержит ссылку на другой в виде оператора <xsl:include>:

Main.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:include href="Included.xsl" />
  ...
  ...
</xsl:stylesheet>

Теперь, если бы я мог загрузить сам файл "Main.xsl" как URI, мой код преобразования был бы таким простым:

// This is a function that works. For demo only.
private string Transform(string xslFileURI)
{
  XslCompiledTransform xslt = new XslCompiledTransform();

  // This load works just fine, if I provide the path to "Main.xsl".
  // The xsl:include is automatically resolved.
  xslTransform.Load(xslFileURI);

  StringWriter sw = new StringWriter();
  xslt.Transform(Server.MapPath("~/XML/input.xml"), null, sw);
  return sw.ToString();
}

Проблема в том, что я получаю содержимое файла Main.xsl в виде строки и мне нужно загрузить строку как XmlReader/IXpathNavigable. Это необходимое ограничение на данный момент. Когда я пытаюсь сделать то же самое, используя XmlReader/XpathDocument, происходит сбой, поскольку код ищет «Included.xsl» в C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\ папка! Очевидно, что XmlResolver не может разрешить относительный URL, потому что он получает только строку в качестве входного XSL.

Мои усилия в этом направлении выглядят так:

// This doesn't work! Halp!
private string Transform(string xslContents)
{
  XslCompiledTransform xslt = new XslCompiledTransform();
  XmlUrlResolver resolver = new XmlUrlResolver();
  resolver.Credentials = CredentialCache.DefaultCredentials;

  //METHOD 1: This method does not work.
  XmlReaderSettings settings = new XmlReaderSettings();
  settings.XmlResolver = resolver;
  XmlReader xR = XmlReader.Create(new StringReader(xslContents), settings);
  xslt.Load(xR);    // fails

  // METHOD 2: Does not work either.
  XPathDocument xpDoc = new XPathDocument(new StringReader(xslContents));
  xslt.Load(xpDoc, new XsltSettings(true, true), resolver);  //fails.

  StringWriter sw = new StringWriter();
  xslt.Transform(Server.MapPath("~/XML/input.xml"), null, sw);
  return sw.ToString();
}

Я пытался использовать метод ResolveUri XmlUrlResolver для получения Stream ссылки на файл XSL, который будет включен, но я не совсем понимаю, как использовать этот поток. IOW, как мне сказать объекту XslCompiledTransform использовать этот поток в дополнение к Main.xsl XmlReader:

Uri mainURI = new Uri(Request.PhysicalApplicationPath + "Main.xsl");
Uri uri = resolver.ResolveUri(mainURI, "Included.xsl");

// I can verify that the Included.xsl file loads in the Stream below.
Stream s = resolver.GetEntity(uri, null, typeof(Stream)) as Stream;

// How do I use this Stream in the function above??


Любая помощь очень ценится. Извините за длинный пост!

Для справки, Exception StackTrace выглядит так:

[FileNotFoundException: Could not find file 'C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\Included.xsl'.]
   System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) +328
   System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) +1038
   System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize) +113
   System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials) +78
   System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn) +51
   System.Xml.Xsl.Xslt.XsltLoader.CreateReader(Uri uri, XmlResolver xmlResolver) +22
   System.Xml.Xsl.Xslt.XsltLoader.LoadStylesheet(Uri uri, Boolean include) +33
   System.Xml.Xsl.Xslt.XsltLoader.LoadInclude() +349
   System.Xml.Xsl.Xslt.XsltLoader.LoadRealStylesheet() +704
   System.Xml.Xsl.Xslt.XsltLoader.LoadDocument() +293
   System.Xml.Xsl.Xslt.XsltLoader.LoadStylesheet(XmlReader reader, Boolean include) +173

Ответы [ 4 ]

5 голосов
/ 15 июня 2009

Использовать пользовательский XmlUrlResolver

class MyXmlUrlResolver : XmlUrlResolver
    {
        public override Uri ResolveUri(Uri baseUri, string relativeUri)
        {
            if (baseUri != null)
                return base.ResolveUri(baseUri, relativeUri);
            else
                return base.ResolveUri(new Uri("http://mypath/"), relativeUri);
        }
    }

И использовать его в функции загрузки XslCompiledTransform,

resolver=new MyXmlUrlResolver();
xslt.Load(xR,null,resolver);
2 голосов
/ 31 июля 2012

Как упоминается в ответе Gee, вы хотите использовать пользовательский XmlResolver (из которого XmlUrlResolver уже получен), но если вы также переопределите метод GetEntity, вы можете в шутку разрешить ссылки в основном документе XSLT и интересные способы. Намеренно простой пример того, как можно разрешить ссылку на Included.xsl :

public class CustomXmlResolver : XmlResolver
{
    public CustomXmlResolver() { }

    public override ICredentials Credentials
    {
        set { }
    }

    public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
    {
        MemoryStream entityStream = null;

        switch (absoluteUri.Scheme)
        {
            case "custom-scheme":

                string absoluteUriOriginalString = absoluteUri.OriginalString;
                string ctgXsltEntityName = absoluteUriOriginalString.Substring(absoluteUriOriginalString.IndexOf(":") + 1);
                string entityXslt = "";

                // TODO: Replace the following with your own code to load data for referenced entities.
                switch (ctgXsltEntityName)
                {
                    case "Included.xsl":
                        entityXslt = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n  <xsl:template name=\"Included\">\n\n  </xsl:template>\n</xsl:stylesheet>";
                        break;
                }

                UTF8Encoding utf8Encoding = new UTF8Encoding();
                byte[] entityBytes = utf8Encoding.GetBytes(entityXslt);
                entityStream = new MemoryStream(entityBytes);

                break;
        }

        return entityStream;
    }

    public override Uri ResolveUri(Uri baseUri, string relativeUri)
    {
        // You might want to resolve all reference URIs using a custom scheme.
        if (baseUri != null)
            return base.ResolveUri(baseUri, relativeUri);
        else
            return new Uri("custom-scheme:" + relativeUri);
    }
}

Когда вы загружаете документ Main.xsl , вы меняете соответствующий код на следующий:

xslt.Load(xpDoc, new XsltSettings(true, true), new CustomXmlResolver());

Приведенный выше пример основан на информации, которую я собрал в статье MSDN Устранение неизвестного: создание пользовательских XmlResolvers в .NET Framework .

2 голосов
/ 15 июня 2009

Я, вероятно, упускаю очевидное, но есть ли причина, по которой вы не просто меняете URI файла Included.xsl на истинный URL? Это можно сделать либо в документе XSL, если у вас есть доступ, либо иначе использовать манипуляции со строками?

0 голосов
/ 19 октября 2015

У меня уже есть успех в преобразованиях, использующих все в памяти:

Наличие xslt, содержащего следующее:

import href = "Common.xslt" и import href = "Xhtml.xslt"

    private string Transform(string styleSheet, string xmlToParse)
            {
                XslCompiledTransform xslt = new XslCompiledTransform();

                MemoryResourceResolver resolver = new MemoryResourceResolver();            


                XmlTextReader xR = new XmlTextReader(new StringReader(styleSheet));           

                xslt.Load(xR, null, resolver);

                StringWriter sw = new StringWriter();                


                using (var inputReader = new StringReader(xmlToParse))
                {
                    var input = new XmlTextReader(inputReader);
                    xslt.Transform(input,
                                        null,
                                        sw);
                }

                return sw.ToString();

            }     

    public class MemoryResourceResolver : XmlResolver
        {

            public override object GetEntity(Uri absoluteUri,
              string role, Type ofObjectToReturn)
            {
                if (absoluteUri.ToString().Contains("Common"))
                {
                    return new MemoryStream(Encoding.UTF8.GetBytes("Xml with with common data"));
                }

                if (absoluteUri.ToString().Contains("Xhtml"))
                {
                    return new MemoryStream(Encoding.UTF8.GetBytes("Xml with with xhtml data"));
                }         

                return "";
            }
        }

Обратите внимание, что абсолютно весь контент представлен в виде строк: styleSheet, xmlToParse и содержимое импорта "Common" и "Xhtml"

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...