使用XslCompiledTransform进行空白剥离

nic*_*elo 8 c# xslt whitespace xslcompiledtransform

我正在尝试将大型应用程序迁移XslTransform到已编译的xsl文件和XslCompiledTransform.

该应用程序使用Xsl创建HTML文件,并将转换数据(Xml)传递给Xsla XmlDataDocument,从数据库返回.

我现在改变所有这一切(至少暂时):

C#

 public string ProcessCompiledXsl(XmlDataDocument xml)
 {
       StringBuilder stringControl = new StringBuilder();
       XslCompiledTransform xslTran = new XslCompiledTransform();

       xslTran.Load(
           System.Reflection.Assembly.Load("CompiledXsl").GetType(dllName)
       );

       xslTran.Transform(xml, this.Arguments, XmlWriter.Create(stringControl, othersettings), null);

       return stringControl.ToString();
 }
Run Code Online (Sandbox Code Playgroud)

XSL(仅举例)

...
  <xsl:output method="html" indent="yes"/>
  <xsl:template match="/">
       <xsl:for-each select="//Object/Table">
              <a href="#">
                     some text
              </a>
       </xsl:for-each>
  </xsl:template>
Run Code Online (Sandbox Code Playgroud)

问题

这是有效的,但xsl正在剥离输出的标签之间的空格:

<a href="#">
   some text
</a><a href="#">
   some text
</a><a href="#">
   some text
</a><a...etc
Run Code Online (Sandbox Code Playgroud)

我试过了:

  • 使用,xml:space="preserve"但我无法让它工作
  • 覆盖了OutputSettings,但我没有得到任何好结果(也许我错过了一些东西)
  • 使用xsl:output method="xml",并且有效,但会创建自闭标签和许多其他问题

所以我不知道该怎么办.也许我没做正确的事.任何帮助都非常感激.

谢谢!

编辑

仅仅为了将来的引用,如果你想解决这个问题,让每个XSL保持不变,那么可以尝试我编写的这个C#类,命名CustomHtmlWriter.

基本上我所做的是扩展XmlTextWriter并修改写入startend标记每个标记的方法.

在这种特殊情况下,您可以像这样使用它:

    StringBuilder sb = new StringBuilder();
    CustomHtmlWriter writer = new CustomHtmlWriter(sb);

    xslTran.Transform(nodeReader, this.Arguments, writer);

    return sb.ToString();
Run Code Online (Sandbox Code Playgroud)

希望它可以帮助某人.

Dim*_*hev 5

一、方案一

我先来分析一下这里的问题

给定这个源 XML 文档(发明的,因为您没有提供任何内容):

<Object>
 <Table>

 </Table>

 <Table>

 </Table>

 <Table>

 </Table>

 <Table>

 </Table>
</Object>
Run Code Online (Sandbox Code Playgroud)

这种转变

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="html" indent="yes"/>

  <xsl:template match="/">
       <xsl:for-each select="//Object/Table">
              <a href="#">
                     some text
              </a>
       </xsl:for-each>
  </xsl:template>
<!--
 <xsl:template match="Table">
   <a href="#">
    Table here
   </a>
 </xsl:template>
 -->
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

准确地重现了问题——结果是:

<a href="#">
                     some text
              </a><a href="#">
                     some text
              </a><a href="#">
                     some text
              </a><a href="#">
                     some text
              </a>
Run Code Online (Sandbox Code Playgroud)

现在,只需取消注释模板并注释掉第一个模板:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="html" indent="yes"/>
<!--
  <xsl:template match="/">
       <xsl:for-each select="//Object/Table">
              <a href="#">
                     some text
              </a>
       </xsl:for-each>
  </xsl:template>
 -->
 <xsl:template match="Table">
   <a href="#">
    Table here
   </a>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

结果具有所需的缩进

 <a href="#">
    Table here
   </a>

 <a href="#">
    Table here
   </a>

 <a href="#">
    Table here
   </a>

 <a href="#">
    Table here
   </a>
Run Code Online (Sandbox Code Playgroud)

这是解决方案 1


二. 解决方案2

此解决方案可以最大限度地减少对现有 XSLT 代码所需的修改:

这是一个两遍转换

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="ext">
 <xsl:output method="html"/>

  <xsl:template match="/">
    <xsl:variable name="vrtfPass1">
       <xsl:for-each select="//Object/Table">
              <a href="#">
                     some text
              </a>
       </xsl:for-each>
    </xsl:variable>

    <xsl:apply-templates select=
        "ext:node-set($vrtfPass1)" mode="pass2"/>
  </xsl:template>

 <xsl:template match="node()|@*" mode="pass2">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*" mode="pass2"/>
  </xsl:copy>
 </xsl:template>

  <xsl:template mode="pass2" match="*[preceding-sibling::node()[1][self::*]]">
   <xsl:text>&#xA;</xsl:text>
   <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

我们的想法是,我们甚至不接触现有代码,而是捕获其输出,并仅使用几行附加代码,将输出格式化为所需的最终外观。

当此转换应用于同一个 XML 文档时,会产生相同的所需结果:

<a href="#">
                     some text
              </a>
<a href="#">
                     some text
              </a>
<a href="#">
                     some text
              </a>
<a href="#">
                     some text
              </a>
Run Code Online (Sandbox Code Playgroud)

最后,这里演示了如何在不触及任何现有 XSLT 代码的情况下引入这一微小更改

让我们将现有代码放入c:\temp\delete\existing.xsl

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="html"/>

  <xsl:template match="/">
    <xsl:for-each select="//Object/Table">
      <a href="#">
        some text
      </a>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

如果我们运行它,我们会得到有问题的输出

现在,我们不再运行existing.xsl,而是运行此转换

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="ext">
 <xsl:import href="file:///c:/temp/delete/existing.xsl"/>
 <xsl:output method="html"/>


  <xsl:template match="/">
    <xsl:variable name="vrtfPass1">
       <xsl:apply-imports/>
    </xsl:variable>

    <xsl:apply-templates select=
        "ext:node-set($vrtfPass1)" mode="pass2"/>
  </xsl:template>

 <xsl:template match="node()|@*" mode="pass2">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*" mode="pass2"/>
  </xsl:copy>
 </xsl:template>

  <xsl:template mode="pass2" match="*[preceding-sibling::node()[1][self::*]]">
   <xsl:text>&#xA;</xsl:text>
   <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

结果就是想要的结果,现有代码根本没有受到任何影响

<a href="#">
        some text
      </a>
<a href="#">
        some text
      </a>
<a href="#">
        some text
      </a>
<a href="#">
        some text
      </a>
Run Code Online (Sandbox Code Playgroud)

解释

  1. 我们导入位于导入优先级层次结构顶层的任何现有代码(未由其他样式表导入),使用xsl:import.

  2. 我们将现有转换的输出捕获到变量中。它具有臭名昭著的 RTF(结果树片段),需要转换为常规树才能进一步处理。

  3. 关键时刻是xsl:apply-imports捕获转换输出时的执行。这确保了现有代码中的任何模板(甚至是我们覆盖的模板,例如模板匹配/)都将被选择执行,就像现有转换自行执行的情况一样)。

  4. 我们使用扩展函数将 RTF 转换为常规树msxsl:node-set()(XslCompiledTransform 还支持EXSLTnode-set()扩展函数)。

  5. 我们对如此制作的普通树进行外观调整。

请注意

这代表了一种在不触及现有代码的情况下对现有转换进行后处理的通用算法