XSL 在 XML CDATA 中保留 Space

XSL Preserve Space in XML CDATA

我尝试了多种解决方案,但似乎无法正常工作。

我正在进行 XML 到 XML (FOP) 的 XSL 转换以创建 PDF。来源 XML 具有 元素,其内容以 CDATA 声明开头。转换删除换行符。

示例输入 XML:

<?xml version="1.0" encoding="iso-8859-1"?>
<myxml>
    <code><![CDATA[import java.nio.charset.Charset;
    import com.my.library.AClass;
    import com.my.library.AnotherClass;

    public String getStringValue(String key) {
            // Just some ramblings

            // Dummy code...
            if (key != null && key.length() > 0) {
                System.out.println(key);
            }
        }
    ]]></code>
</myxml>

XSL 示例:

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns="http://www.w3.org/TR/xhtml1/strict" 
    xmlns:fo="http://www.w3.org/1999/XSL/Format" 
    exclude-result-prefixes="fo" 
    >
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" cdata-section-elements="code"/>
<xsl:preserve-space elements="code" />

<fo:block font-family="Courier New" font-size="12pt" color="black" 
    space-after="12pt" space-before="12pt" space-before.precedence="4">

      <fo:block>
          <xsl:text>Copy</xsl:text>
          <xsl:copy>
            <xsl:value-of select="code"/>
          </xsl:copy>
      </fo:block>

      <fo:block>
          <xsl:text>Copy Text</xsl:text>
          <xsl:copy>
            <xsl:value-of select="code/text()"/>
          </xsl:copy>
      </fo:block>

      <fo:block>
          <xsl:text>Original</xsl:text>
          <xsl:value-of select="code"/>
      </fo:block>

      <fo:block>
          <xsl:text>Normalise space</xsl:text>
          <value-of select="normalize-space(code)" disable-output-escaping="yes"/>
      </fo:block>

      <fo:block>          
          <xsl:text>Copy with extra CDATA wrapper?</xsl:text>
          <xsl:copy>
            <xsl:text disable-output-escaping="yes"><![CDATA[<![CDATA[]]></xsl:text>
            <xsl:value-of select="code"/>
            <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
          </xsl:copy>         
      </fo:block>

      <fo:block>
        <xsl:text>Again, an attempt to wrap</xsl:text>
        <xsl:text disable-output-escaping="yes">
          &lt;![CDATA[
        </xsl:text>
        <xsl:value-of select="code" />
        <xsl:text disable-output-escaping="yes">
          ]]&gt;
        </xsl:text>
      </fo:block>
</fo:block>

排列... 我已经尝试了以下所有排列:

  • 全局声明:同时使用 cdata-section 和 preserve-space,仅使用其中之一
  • 来源 xml 文档 containing/not 在代码元素和外部元素上都包含 xml:space="preserve"。

一共6个排列。

这些设置对我来说似乎没什么区别。以下是每个测试块的输出:

  • 块'Copy':空(无文本输出)
  • 块'Copy Text':空(无文本输出)
  • 块'Original':代码,但所有换行符都被删除
  • 块'Normalise space':空(无文本输出)
  • 块'Copy with extra CDATA wrapper?':空(无文本输出)
  • 块'Again, an attempt to wrap':代码,但删除了所有换行符

让我们澄清一下您的代码中反映的一些基本误解。

  1. 如@michael.hor257k 所述,您没有 xsl:template 结构体。这对于简化的样式表来说实际上是可以的 你正在尝试,但这种形式没有 xsl:stylesheet 根 元素;它只是以 XML 根元素开始 输出 XML。然后它从输入中提取 XML 正如你正在做的那样 xsl:value-of/@select 构造。所以,主要修复 #1 是添加 <xsl:template match="/">... 或删除 <xsl:stylesheet>。出色地 选择第二个选项(简化的样式表),因为它似乎是您喜欢的。
  2. 下一步,意识到你的 XPaths 没有 select 任何东西,所以 你所有的测试用例都没有证明什么。主要修复#2:而不是 selecting "code/', select "/myxml/code" 在你所有的测试中 案例。

以下是经过上述更改(以及其他一些小修复)的测试用例:

<?xml version="1.0" encoding="iso-8859-1"?>
<fo:block font-family="Courier New" font-size="12pt" color="black" 
          space-after="12pt" space-before="12pt" space-before.precedence="4"
          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
          xmlns:fo="http://www.w3.org/1999/XSL/Format"
          xsl:version="1.0">
  <fo:block>
    <xsl:text>Copy</xsl:text>
    <xsl:copy>
      <xsl:value-of select="/myxml/code"/>
    </xsl:copy>
  </fo:block>
  <fo:block>
    <xsl:text>Copy Text</xsl:text>
    <xsl:copy>
      <xsl:value-of select="/myxml/code/text()"/>
    </xsl:copy>
  </fo:block>
  <fo:block>
    <xsl:text>Original</xsl:text>
    <xsl:value-of select="/myxml/code"/>
  </fo:block>
  <fo:block>
    <xsl:text>Normalise space</xsl:text>
    <xsl:value-of select="normalize-space(code)" disable-output-escaping="yes"/>
  </fo:block>
  <fo:block>          
    <xsl:text>Copy with extra CDATA wrapper?</xsl:text>
    <xsl:copy>
      <xsl:text disable-output-escaping="yes"><![CDATA[<![CDATA[]]></xsl:text>
      <xsl:value-of select="/myxml/code"/>
      <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
    </xsl:copy>         
  </fo:block>
  <fo:block>
    <xsl:text>Again, an attempt to wrap</xsl:text>
    <xsl:text disable-output-escaping="yes">
      &lt;![CDATA[
    </xsl:text>
    <xsl:value-of select="/myxml/code" />
    <xsl:text disable-output-escaping="yes">
      ]]&gt;
    </xsl:text>
  </fo:block>
</fo:block>

运行 以上 XSLT 针对您的输入 XML 产生更有用的测试结果:

<?xml version="1.0" encoding="UTF-8"?><fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="Courier New" font-size="12pt" color="black" space-after="12pt" space-before="12pt" space-before.precedence="4"><fo:block>Copyimport java.nio.charset.Charset;
    import com.my.library.AClass;
    import com.my.library.AnotherClass;

    public String getStringValue(String key) {
            // Just some ramblings

            // Dummy code...
            if (key != null &amp;&amp; key.length() &gt; 0) {
                System.out.println(key);
            }
        }
    </fo:block><fo:block>Copy Textimport java.nio.charset.Charset;
    import com.my.library.AClass;
    import com.my.library.AnotherClass;

    public String getStringValue(String key) {
            // Just some ramblings

            // Dummy code...
            if (key != null &amp;&amp; key.length() &gt; 0) {
                System.out.println(key);
            }
        }
    </fo:block><fo:block>Originalimport java.nio.charset.Charset;
    import com.my.library.AClass;
    import com.my.library.AnotherClass;

    public String getStringValue(String key) {
            // Just some ramblings

            // Dummy code...
            if (key != null &amp;&amp; key.length() &gt; 0) {
                System.out.println(key);
            }
        }
    </fo:block><fo:block>Normalise space</fo:block><fo:block>Copy with extra CDATA wrapper?<![CDATA[import java.nio.charset.Charset;
    import com.my.library.AClass;
    import com.my.library.AnotherClass;

    public String getStringValue(String key) {
            // Just some ramblings

            // Dummy code...
            if (key != null &amp;&amp; key.length() &gt; 0) {
                System.out.println(key);
            }
        }
    ]]></fo:block><fo:block>Again, an attempt to wrap
      <![CDATA[
    import java.nio.charset.Charset;
    import com.my.library.AClass;
    import com.my.library.AnotherClass;

    public String getStringValue(String key) {
            // Just some ramblings

            // Dummy code...
            if (key != null &amp;&amp; key.length() &gt; 0) {
                System.out.println(key);
            }
        }

      ]]>
    </fo:block></fo:block>

我相信从那里您将能够确定产生您寻求的结果的测试用例结果。

将您的代码放在 fo:block 中并设置以下属性来控制空格和回车符的处理 returns:

像这样:

<fo:block>
    <xsl:text>Original</xsl:text>
    <fo:block linefeed-treatment="preserve" 
              white-space-collapse="false" 
              white-space-treatment="preserve">
        <xsl:value-of select="code"/>
    </fo:block>
</fo:block>

(代表 OP 发布。此解决方案是对问题的编辑,因此我已回滚该编辑并在此处发布。)

输入XML:

<?xml version="1.0" encoding="iso-8859-1"?>
<solution>
   <solution_name>A Set of Functions</solution_name>
   <description>Just for the sake of example</description>
   <functions>
      <function>
        <code><![CDATA[function String getStringSafely(String textToCheck, String defaultValue) {
                if (textToCheck == null || textToCheck.length() <= 0) {
                    return defaultValue;
                }
                return textToCheck;
            }
        ]]></code>
      </function>
      <function><code><![CDATA[import java.nio.charset.Charset;
import com.example.library.Something;

function String doSomething(String key) {
        if (key == null || key.length() <= 0) {
            return "";
        }
        System.out.println(String.format("Yep, got a value[%1$s]", key));
    }
]]></code>
      </function>
    </functions>
</solution>

XSL 示例:

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns="http://www.w3.org/TR/xhtml1/strict" 
    xmlns:fo="http://www.w3.org/1999/XSL/Format" 
    exclude-result-prefixes="fo" 
    >
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" cdata-section-elements="code"/>
<xsl:preserve-space elements="code" />
<xsl:template match="solution">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <fo:layout-master-set>
    <fo:simple-page-master master-name="solution-page">
      <fo:region-body margin="1in"/>
    </fo:simple-page-master>
  </fo:layout-master-set>

  <fo:page-sequence master-reference="solution-page">
    <fo:flow flow-name="xsl-region-body" id="solution">
        <fo:block text-align="center" 
        space-after="40pt" space-before="100pt" space-after.precedence="3" 
        font-family="Helvetica" font-weight="bold" font-size="14pt" color="#0050B2" >
                <xsl:value-of select="solution_name"/>
        </fo:block>
        <fo:block font-family="Helvetica" font-size="12pt" color="black" 
    space-after="12pt" space-before="12pt" space-before.precedence="4">
            <xsl:value-of select="description"/>
        </fo:block>
        <xsl:for-each select="functions">
            <xsl:variable name="this" select="." />
            <xsl:call-template name="solution_functions">
                <xsl:with-param name="data" select="$this"/>
            </xsl:call-template>
        </xsl:for-each>
    </fo:flow>
  </fo:page-sequence>
</fo:root>
</xsl:template>

<xsl:template name="solution_functions">
    <xsl:param name="data"/>
  <fo:block id="functions" break-before="page" 
            font-family="Helvetica"
            font-weight="bold"
            font-size="14pt"
            color="#0050B2" space-after="12pt"
            space-before="16pt" space-after.precedence="3">
        Functions
  </fo:block>
    <xsl:for-each select="$data/function">
        <xsl:variable name="this" select="." />
        <xsl:call-template name="present_function">
            <xsl:with-param name="data" select="$this"/>
        </xsl:call-template>
    </xsl:for-each>
</xsl:template>

<xsl:template name="present_function">
    <xsl:param name="data"/>
    <fo:block 
                font-family="Helvetica"
                font-weight="bold"
                font-size="12pt" 
                space-after="12pt"
                space-before="16pt" space-after.precedence="3">
            Code
    </fo:block>
    <fo:block font-family="Helvetica" font-size="12pt" color="black" 
        space-after="12pt" space-before="12pt" space-before.precedence="4">
        <fo:block>
            <xsl:text>This works! Thanks ...</xsl:text>
            <fo:block linefeed-treatment="preserve" 
                      white-space-collapse="false" 
                      white-space-treatment="preserve">
                <xsl:value-of select="$data/code"/>
            </fo:block>
        </fo:block>
    <fo:block>
       [ All following examples were part of the original post. The above is the one which works. The below, kept for future reference to original question. ]
    </fo:block>
    <fo:block>
        <xsl:text>Original</xsl:text>
        <fo:block linefeed-treatment="preserve" 
                  white-space-collapse="false" 
                  white-space-treatment="preserve">
            <xsl:value-of select="$data/code"/>
        </fo:block>
    </fo:block>

      <fo:block>
          <xsl:text>Copy</xsl:text>
          <xsl:copy>
            <xsl:value-of select="$data/code"/>
          </xsl:copy>
      </fo:block>

      <fo:block>
          <xsl:text>Copy Text</xsl:text>
          <xsl:copy>
            <xsl:value-of select="$data/code/text()"/>
          </xsl:copy>
      </fo:block>

      <fo:block>
          <xsl:text>Original</xsl:text>
          <xsl:value-of select="$data/code"/>
      </fo:block>

      <fo:block>
          <xsl:text>Normalise space</xsl:text>
          <value-of select="normalize-space($data/code)" disable-output-escaping="yes"/>
      </fo:block>

      <fo:block>          
          <xsl:text>Copy with extra CDATA wrapper?</xsl:text>
          <xsl:copy>
            <xsl:text disable-output-escaping="yes"><![CDATA[<![CDATA[]]></xsl:text>
            <xsl:value-of select="$data/code"/>
            <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
          </xsl:copy>         
      </fo:block>

      <fo:block>
        <xsl:text>Again, an attempt to wrap</xsl:text>
        <xsl:text disable-output-escaping="yes">
          &lt;![CDATA[
        </xsl:text>
        <xsl:value-of select="$data/code" />
        <xsl:text disable-output-escaping="yes">
          ]]&gt;
        </xsl:text>
      </fo:block>

    </fo:block>
</xsl:template>
</xsl:stylesheet>