【问题标题】:How to chain/nest XSL Transforms in .NET如何在 .NET 中链接/嵌套 XSL 转换
【发布时间】:2011-04-13 06:06:05
【问题描述】:

上下文:.NET Framework 3.5

我意识到如何使用 XSLT 执行单个 XML 转换,但没有找到任何关于链接 XML 转换的好示例。

输入: - XML 文档作为 XPathDocument。 - 多个 XSL 文件的文件路径。

预期输出: - 最好是 XPathDocument/IXPathNavigable,一一表示应用了所有转换的 XML。

示例场景

输入xml:<doc></doc>

xsl-1:将<one /> 添加为 doc 元素的子元素的 .xsl。 xsl-2:将<two /> 添加为 doc 元素的子元素的 .xsl。

预期结果

<doc><one /><two /></doc>

目标

利用 XPathDocument/IXPathNavigable 或更好的向前特性。避免将整个文档加载到内存中。

【问题讨论】:

  • 我认为你无法实现你的目标。 XPath 表达式(如//somenode)将搜索整个文档树,因此需要加载整个文档。
  • 如果事先不知道样式表的 URI,这不能用纯 XSLT 完成:您不能动态地包含或导入样式表。您可以使用 Dimitre 的 FXSL 中的模板参考。您也可以静态声明您的样式表导入链。否则,这个问题严格关注.net
  • 如果要避免将整个文档加载到内存中,则不能使用 XSLT。 XSLT 转换不是只向前的——如果是,你就不能做像<xsl:value-of select='count(//*)'/> 这样的事情。
  • 关于您的“预期输出... IXPathNavigable”,请参阅this question

标签: .net xml .net-3.5 xslt


【解决方案1】:

可能类似于以下内容(我没有尝试编译):

XslCompiledTransform xsl1 = new XslCompiledTransform();
xsl1.Load("xsl1.xsl");

XslCompiledTransform xsl2 = new XslCompiledTransform();
xsl1.Load("xsl2.xsl");

using (Stream stream = new MemoryStream())
{
     using (XmlReader xmlReader1 = XmlReader.Create("source.xml"))
     {
          xsl1.Transform(xmlReader1, stream);
     }

     stream1.Position = 0;

     using (XmlReader xmlReader2 = XmlReader.Create(stream))
     {
         xsl2.Transform(xmlReader2, "output.xml");
     }
}

通过使用 xmlreader,您将获得您正在寻找的转发。我刚刚将第一个结果输出到 MemoryStream,但您可以对临时文件执行此操作。

为了获得额外的一点性能,您可能需要查看预编译 xslt。

XSLT Compiler (xsltc.exe)

【讨论】:

    【解决方案2】:

    我。这演示了如何使用支持 exslt node-set() 扩展函数(包括 .NET XslCompiledTransform)的任何 XSLT 1.0 处理器执行多通道 XSLT 1.0 转换。

    对于其他 XSLT 1.0 处理器,需要将 ext:node-set() 替换为它们支持的任何内容,例如用于 MSXML 的 msxsl:node-set()(msxsl 与正确的命名空间相关联)。

    <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:ext="http://exslt.org/common"
        exclude-result-prefixes="ext xsl">
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="node()|@*" name="identity">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match="/">
      <xsl:variable name="vrtfPass1">
       <xsl:apply-templates select="node()"/>
      </xsl:variable>
    
      <xsl:apply-templates mode="pass2"
       select="ext:node-set($vrtfPass1)/node()"/>
     </xsl:template>
    
     <xsl:template match="/*">
         <xsl:copy>
           <xsl:copy-of select="@*"/>
           <one/>
         <xsl:apply-templates/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match="node()|@*" mode="pass2">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*" mode="pass2"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match="/*/one" mode="pass2" >
         <xsl:call-template name="identity"/>
          <two/>
     </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于此 XML 文档时

    <doc/>
    

    产生想要的结果

    <doc>
       <one/>
       <two/>
    </doc>
    

    二。 目前 XSLT 1.0 和 XSLT 2.0 使用 XPath,这要求整个 XML 文档(表示)都在 RAM 中

    Working Draft for XSLT 2.1(最近重命名为 3.0)提出了一个新的 streaming feature,如果此 WD 成为官方推荐,它将使兼容 XSLT 3.0 处理器来实现流。

    三。 XPathDocument 的 XslCompiledTransform 仍将整个 XML 文档表示形式保存在内存中XPathDocument 提供只向前迭代的事实并不意味着 XslCompiledTransform 执行 XML 文档的任何流处理。

    【讨论】:

    • +1,我不明白为什么这被否决了。第一点可能超出了这个问题,但这个答案仍然是正确的并且非常有帮助。
    • 一般来说可能会有所帮助,事实上我可以自己推断 xslt 用于当前的一些工作,但它根本不能回答这个问题。它演示了如何使用纯 xslt 将多个 xslt 组合成一个,而不是像使用 .NET 那样将它们链接起来。鉴于 OP 使用 .NET 3.5,XSLT3 的当前状态虽然很有趣,但无关紧要。 OP 有具体的投入和目标,而这个答案甚至没有试图实现它们。我知道为什么它被否决了,但不是我这样做,我没有明白这一点。
    • @Flynn1179:答案清楚地表明无法实现预期目标,并给出了未来的前景,表明这确实是即将到来的 XSLT 版本中解决的问题。我不明白您为什么会对此投反对票(我不会因为答案是“对不起,您不能那样做”而投反对票)。
    • @0xA3:感谢您对我的回答的正确理解。在其中,我尽我所知提供了请求转换链的替代方法——在 XSLT 中,我正在解释为什么今天不能使用 XSLT 技术来完成。我还对即将推出的 XSLT 3.0 流提供了一个观点。至于@Flynn1179 否决正确答案,他以前一直这样做——至少对我的答案来说——这仅反映了他对 XSLT 的特定理解水平,以及他自己的个性。
    • 哦,长大了,迪米特。我只对你的一个答案投了反对票,如果不是错的话,我也不会这样做。但是,您并没有纠正您的错误,而是删除了删除我的 cmets 的答案(以及在您称我为懦夫后斥责您进行人身攻击的版主的 cmets),然后重新发布了相同的错误答案。从那时起,直到今天我都不再对您的帖子发表评论/投票,即使在这里我也评论说它很有用,并试图解释为什么有人可能会否决它。你真的需要停止人身攻击。
    【解决方案3】:

    如果您想避免将整个文档树加载到内存中,您可能需要考虑一种不同于 XSLT 的技术。

    有一种方法叫做Streaming Transformations for XML (STX),它不需要构建内存树。您可能想查看 SourceForge 上的STX project

    有关流转换的概述,我推荐以下文章:

    An Introduction to Streaming Transformations for XML

    【讨论】:

    • 投反对票,想解释一下吗?我的回答可能不是对 OP 问题的直接回答,但它显示了实现基于流的转换目标的另一种方法。 STX 不是“死”技术,它是例如被多个框架(例如 Cocoon)使用,并且与 XSLT 中计划的流式传输功能不同,它现在已经可以使用(尽管它是一种不同的技术并且比 XSLT 更受限制)。
    猜你喜欢
    • 2015-05-11
    • 1970-01-01
    • 2017-08-22
    • 2019-08-28
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-06
    相关资源
    最近更新 更多