如何解析从字符串加载 XSL 的转换中包含的 XSL?

发布于 2024-07-24 06:52:09 字数 3894 浏览 6 评论 0原文

.NET 2.0/VS2005

我正在尝试使用 XslCompiledTransform 类来执行 XSL 转换。 我有两个 XSL 文件,第一个以 语句的形式包含对另一个文件的引用:

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 执行相同操作时,它会失败,因为代码会查找C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\ 文件夹中的“included.xsl”! 显然,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();
}

我尝试使用 XmlUrlResolver 的 ResolveUri 方法来获取引用 XSL 文件的 Stream包括,但我对如何使用此流感到困惑。 IOW,除了 Main.xsl XmlReader 之外,如何告诉 XslCompiledTransform 对象使用此流:

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


任何帮助是极大的赞赏。 抱歉帖子太长了!

供您参考,异常 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

.NET 2.0/VS2005

I am trying to use the XslCompiledTransform class to perform a XSL Transformation. I have two XSL files, the first of which includes a reference to the other in the form of an <xsl:include> statement :

Main.xsl:

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

Now, If I could load the "Main.xsl" file itself as a URI, my transformation code would be as simple as :

// 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();
}

The problem is that I receive the contents of the Main.xsl file as a string and need to load the string as an XmlReader/IXpathNavigable. This is a necessary restriction at this time. When I try to do the same using an XmlReader/XpathDocument, it fails because the code looks for "Included.xsl" in the C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\ folder! Obviously, the XmlResolver is not able to resolve the relative URL because it only receives a string as input XSL.

My efforts in this direction look like:

// 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();
}

I have tried to use the ResolveUri method of the XmlUrlResolver to obtain a Stream referencing the XSL file to be included, but am confused as to how to use this Stream. IOW, how do I tell the XslCompiledTransform object to use this stream in addition to the 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??

Any help is greatly appreciated. Sorry for the long post!

For your reference, the Exception StackTrace looks like this:

[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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧话新听 2024-07-31 06:52:09

使用自定义的 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);

Use a custom 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);
        }
    }

And use it in load function of XslCompiledTransform,

resolver=new MyXmlUrlResolver();
xslt.Load(xR,null,resolver);
背叛残局 2024-07-31 06:52:09

正如 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 文章 解决未知问题:构建中获取的信息.NET Framework 中的自定义 XmlResolvers

As Gee's answer mentions, you want to use a custom XmlResolver (of which XmlUrlResolver is already derived), but if you also override the method GetEntity you can resolve references in the primary XSLT document in fun and interesting ways. A deliberately simple example of how you could resolve the reference to 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);
    }
}

When you load the Main.xsl document you'd change the relevant code to the following:

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

The above example was based on info I picked-up in the MSDN article Resolving the Unknown: Building Custom XmlResolvers in the .NET Framework.

情深已缘浅 2024-07-31 06:52:09

我可能错过了显而易见的事情,但是您是否有理由不将 Included.xsl 的 URI 更改为真正的 URL? 如果您有权访问,这可以在 XSL 文档中完成,否则可以使用字符串操作?

I am probably missing the obvious but is there a reason you don't just change the URI of Included.xsl to be a true URL? This could either be done in the XSL doc if you have access or using string manipulation otherwise?

呆° 2024-07-31 06:52:09

我已经成功地使用内存中的所有内容进行转换:

拥有包含以下内容的 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”导入的内容

I already have success with doing transformations using all in memory:

Having a xslt containing the following includes:

import href="Common.xslt" and
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 "";
            }
        }

Note that absolutely all content is as strings: styleSheet, xmlToParse and the content of the "Common" and "Xhtml" imports

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文