10

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

该应用程序使用Xsl来创建 HTML 文件,并且转换数据 ( Xml) 被传递给XslXmlDataDocument数据库返回的 。

我已经改变了这一切,所以现在我做了(至少是暂时的):

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

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>

问题

这行得通,但 xsl 正在剥离标签输出之间的空格:

<a href="#">
   some text
</a><a href="#">
   some text
</a><a href="#">
   some text
</a><a...etc

我试过了:

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

所以我不知道该怎么办。也许我做的不对。非常感谢任何帮助。

谢谢!

编辑

只是为了将来参考,如果你想解决这个问题,让每个 XSL 保持不变,可以试试我写的这个 C# 类,命名为CustomHtmlWriter.

基本上我所做的是扩展XmlTextWriter和修改编写每个标签的start方法end

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

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

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

    return sb.ToString();

希望它可以帮助某人。

4

4 回答 4

5

一、方案一

我先分析一下这里的问题

鉴于此源 XML 文档(发明,因为您没有提供任何):

<Object>
 <Table>

 </Table>

 <Table>

 </Table>

 <Table>

 </Table>

 <Table>

 </Table>
</Object>

这种转变

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

完全重现了这个问题——结果是:

<a href="#">
                     some text
              </a><a href="#">
                     some text
              </a><a href="#">
                     some text
              </a><a href="#">
                     some text
              </a>

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

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

结果有想要的缩进

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

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

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

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

这是解决方案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>

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

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

<a href="#">
                     some text
              </a>
<a href="#">
                     some text
              </a>
<a href="#">
                     some text
              </a>
<a href="#">
                     some text
              </a>

最后,这里演示了如何在不涉及任何现有 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>

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

现在,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>

结果是想要的,现有的代码根本没有改变

<a href="#">
        some text
      </a>
<a href="#">
        some text
      </a>
<a href="#">
        some text
      </a>
<a href="#">
        some text
      </a>

说明

  1. 我们使用xsl:import.

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

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

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

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

请注意

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

于 2012-09-02T22:12:06.297 回答
1

我认为问题是:

  <xsl:output method="html" indent="yes"/> 

如果我没记错的话,html 会尝试只关心对 HTML 的显示方式很重要的空格。

如果你试试:

  <xsl:output method="xml" indent="yes"/> 

然后它应该创建您期望的缩进空白。

于 2012-08-31T16:37:01.657 回答
1

我不记得我头顶上的 XML/XSLT 空间保留的细节,但它更有可能丢弃空白的一个实例是在没有非空白文本的元素之间(即只有空白的文本节点,如</a>和之间的那个</xsl:for-each>)。您可以通过使用该<xsl:text>元素来防止这种情况。

例如,之后

          <a href="#">
                 some text
          </a>

          <xsl:text>&#10;</xsl:text>

即文字行结束符。

这符合你的要求吗?

于 2012-08-31T16:03:22.023 回答
1

样式表中的空白文本节点总是被忽略,除非它们包含在 xsl:text 中。如果要将空格输出到结果树,请使用 xsl:text。

(也可以在样式表中使用 xml:space="preserve",但通常不建议这样做,因为它会产生不必要的副作用。)

于 2012-08-31T18:36:13.140 回答