7

作为我们 Web 应用程序构建过程的一部分,我已经设置了我们的 XSLT 样式表,以便在我们运行完整编译时使用Microsoft 的 xsltc.exe编译器构建。在本地开发期间,这非常有效,因为代码是在同一位置编译和托管的。但是,一旦将其放在构建服务器上,就会出现问题。

构建服务器将像我在本地那样编译 XSLT 样式表,但随后会运行一个脚本,将编译后的代码部署到我们的内部登台 Web 服务器。一旦这些二进制文件从它们的编译位置移出,其中的相对路径<xsl:import><xsl:include>元素就不再正确解析,从而导致在运行 XSLT 样式表时出现类似这样的异常。

Could not find a part of the path 'e:\{PATH}\xslt\docbook\VERSION'.
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    at 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, Boolean useLongPath)
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy)
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize)
    at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
    at System.Xml.Xsl.Runtime.XmlQueryContext.GetDataSource(String uriRelative, String uriBase)

这是现在代码的一般概念:

var xslt = new XslCompiledTransform();
xslt.Load(typeof(Namespace.XslTransforms.CompiledXsltStylesheet));
xslt.Transform("input.xml", "output.xml");

现在,我使用带有单个“Type”参数的 XslCompiledTransform.Load() 方法来引入基于 xsltc.exe 的预编译 XSLT 样式表。我可以从堆栈跟踪中得知 .NET 框架正在使用 XmlUrlResolver 来尝试解析这些外部样式表的实际位置,但我没有看到提供重写的 XmlResolver 实现的方法,我可以在其中传入一个新的baseUri 指向这些样式表在 Web 服务器上的位置。

我假设我可以通过不再使用 xsltc.exe 预编译并通过 XmlReaders 加载 XSLT 样式表来解决这个问题,因为这将让我使用其他 XslCompiledTransform.Load() 方法,这些方法有一个参数,我可以提供我自己的 XmlResolver 实现. 但是,我喜欢用于语法验证和性能的预编译选项,所以我不想放弃它,除非我绝对必须这样做。

有没有办法使用 xsltc.exe 来预编译这些 XSLT 样式表,但仍然提供一种方法来显式声明 baseUri 以在运行时<xsl:include><xsl:import>元素的相对路径解析?

4

3 回答 3

5

在玩了很多之后,我发现我提供的代码自动使用System.Xml.XmlUrlResolver在运行时解析路径<xsl:include><xsl:import>相对路径是正确的。但是,当 XmlUrlResolver 被放在二进制文件中时,它的使用不会绑定到System.Xml.XslCompiledTransformxsltc.exeXmlResolver 实际上是由在运行时执行转换的System.Xml.XmlReader 上的 System.Xml.XmlReaderSettingsXmlResolver属性选择的。在我使用的 XsltReaderSettings 上设置我自己的自定义 XmlResolver 后,我就能够控制相对路径分辨率。

如果你想像我一样重写这个 XmlResolver,下面的代码可以作为指导:

var customXmlResolver = new SomeCustomXmlResolver();  // Derives from XmlResolver
var xmlReaderSettings = new XmlReaderSettings {
  XmlResolver = customXmlResolver
};

var xslt = new XslCompiledTransform();
xslt.Load(typeof(Namespace.XslTransforms.CompiledXsltStylesheet));

using (var xmlReader = XmlReader.Create("input.xml", xmlReaderSettings)) {
  using (var xmlWriter = XmlWriter.Create("output.xml")) {
    xslt.Transform(xmlReader, null, xmlWriter, customXmlResolver);
  }
}

我仍在使用xsltc.exe来编译我的 XSLT 样式表,但是当我在 Web 服务器上加载这些编译后的样式表时,注入SomeCustomXmlResolver的代码会重写覆盖的路径ResolveUri()GetEntity()方法,以便可以找到存在于基于<xsl:include>和 -的相对路径中的引用文件。<xsl:import>作为额外的好处,通过将相同的 XmlResolver 添加到Transform()方法的末尾, document()XML 中的操作也将正确解析它们的相对路径。

于 2012-04-04T03:59:51.537 回答
2

有没有办法使用 xsltc.exe 来预编译这些 XSLT 样式表,但仍然提供一种方法来显式声明 baseUri 以在运行时<xsl:include><xsl:import>元素的相对路径解析?

尝试使用

XslCompiledTransform.CompileToType()

此静态方法接受的参数之一是

XmlResolver stylesheetResolver
于 2012-01-25T03:15:09.763 回答
0

我不知道这是否会破坏您的系统,但是如何代替

  1. 编译xsltc.exe
  2. 部署二进制文件
  3. 用这个加载二进制文件Load()

  1. import/include部署样式表,但是指令需要很多样式表
  2. 用这个Load()加载主样式表,指定解析器import/incldue

至少在运行时,您似乎仍然可以从“已编译”样式表中受益。

于 2012-01-24T22:36:36.650 回答