如何在.NET中链接/嵌套XSL转换

3

背景:.NET Framework 3.5

我知道如何使用XSLT执行单个XML转换,但没有找到关于链接XML转换的好例子。

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

期望输出: - 最好是XPathDocument/IXPathNavigable格式,表示已应用所有转换的XML,一个接一个。

示例场景

输入的xml:<doc></doc>

xsl-1: .xsl文件,在doc元素下添加<one />子元素。 xsl-2: .xsl文件,在doc元素下添加<two />子元素。

期望结果

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

目标

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


1
我认为你无法实现你的目标。XPath表达式(例如//somenode)将搜索整个文档树,因此需要加载整个文档。 - Dirk Vollmar
1
如果样式表的URI事先不知道,那么就不能使用纯XSLT来完成:无法动态地包含或导入样式表。您可以像Dimitre的FXSL中那样使用模板引用。此外,您可以静态声明样式表导入链。否则,这个问题严格涉及.NET。 - user357812
1
如果您想避免在内存中加载整个文档,则无法使用XSLT。 XSLT转换并非仅向前 - 如果是这样,您无法执行类似<xsl:value-of select='count(//*)'/>的操作。 - Robert Rossney
关于您的“Expected output... IXPathNavigable”的问题,请参见这个问题 - harpo
3个回答

2
也许可以尝试以下代码(我没有尝试编译过):
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编译器(xsltc.exe)


0

I. 这演示了如何使用支持 exslt node-set() 扩展函数的任何 XSLT 1.0 处理器(包括 .NET XslCompiledTransform)执行多遍 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中。

XSLT 2.1的工作草案(最近更名为3.0)提出了一个新的流式处理特性,如果此工作草案成为官方建议,那么符合XSLT 3.0处理器的要求可以实现流式处理。

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


+1,我不明白为什么这个被踩了。第一点可能超出了问题的范围,但是这个答案是正确且非常有帮助的。 - Dirk Vollmar
或许总体上有帮助,实际上我可以为一些当前的工作自己推断出XSLT,但它根本没有回答这个问题。它演示了如何使用纯XSLT将多个XSLT组合成一个,而不是像.NET那样链式使用它们。鉴于OP正在使用.NET 3.5,XSLT3的当前状态虽然有趣,但是无关紧要。OP有特定的输入和目标,而这个答案甚至没有尝试去实现它们。我能看出来为什么它被踩了,但不是我踩的,我没看到它的意义。 - Flynn1179
@Flynn1179:答案明确指出所需目标无法实现,并给出了未来的展望,表明这确实是即将在XSLT版本中解决的问题。我不明白为什么你要对此进行负投票(我不会因为答案是“抱歉,你不能这样做”而进行负投票)。 - Dirk Vollmar
@0xA3:感谢您正确理解我的回答。在其中,我尽我所知提供了一种替代请求转换链的方法——在XSLT中,并解释了为什么今天无法使用XSLT技术来完成此操作。我还提供了即将推出的XSLT 3.0流式处理的视角。至于@Flynn1179对正确答案的投票否决,他之前一直这样做——至少是针对我的答案——这只反映了他对XSLT的特定理解水平,以及他自己的个性。 - Dimitre Novatchev
哦,长大点,迪米特。我只曾给你的一个回答点过踩,如果它没有错误的话,我也不会这样做。然而,你并没有纠正你的错误,而是删除了回答以消除我的评论(还有那位在你称我为懦夫后责备你进行人身攻击的版主的评论),然后重新发布了同样错误的回答。从那时起,我就避免在你的帖子上发表评论/投票,直到今天,即使在这里,我也评论说它很有用,并试图解释为什么有人可能会对其点踩。你真的需要停止进行人身攻击。 - Flynn1179
@0xA3:只是想指出,我并没有说我会给它点踩,我只是试图提供一个可能的解释,为什么有人会这样做。通常情况下,我只会给那些明显错误的答案点踩。 - Flynn1179

0

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

有一种称为XML流式转换(STX)的方法,它不需要构建内存树。您可以在SourceForge上查看STX项目

关于流式转换的概述,我推荐阅读以下文章:

XML流式转换简介


投票负责人,能否解释一下?我的回答可能不是针对 OP 问题的直接回答,但它展示了实现流式转换目标的另一种方式。STX 不是一个“死”技术,例如被 Cocoon 等多个框架使用,并且与 XSLT 中计划的流媒体功能不同,它已经可以在今天使用(尽管它是一种不同的技术,并且比 XSLT 更有限)。 - Dirk Vollmar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接