复制文本并替换 XSL 中的字符

Copy text and replace character in XSL

我正在将 DITA 文档转换为基于格式的简化 XML 以用作导入到 Adob​​e InDesign 中。我的转换进行得非常顺利,除了一个元素在输出中省略了文本。该元素是 codeblock。当我根本没有指定它的模板时,元素和任何子元素都会传递到新的 XML 文档,但会传递文本的 none。此元素应与文本 子元素一起传递,就像我的文档中未定义特定模板的所有其他元素一样。 XSL 样式表中没有任何其他地方指定 codeblock 或其任何属性。我完全被难住了,无法弄清楚这里发生了什么。

还值得注意的是,许多内联元素(cmdnameparmnameuserinput 等)在输出时被转换为 bold。下游XML用于格式化,不需要知道语义上下文。

这就是我要通过的内容:

<codeblock>This is the first line of my code block.
This is my second line to prove that line feeds are preserved.
This line proves that <parmname>child elements</parmname> are passed through.</codeblock>

没有为 codeblock 定义模板,这就是我得到的结果:

<codeblock><bold/></codeblock>

我想要的实际结果是:

<codeblock>This is the first line of my code block.&#8232;This is my second line to prove that line feeds are preserved.&#8232;This line proves that <bold>child elements</bold> are passed through.</codeblock>

我需要用字符实体替换换行符,因为 InDesign 会将任何不以元素开头的新行视为分栏符。我的目标是使用以下模板简单地用 &#8232; 替换换行符:

<xsl:template match="codeblock//text()">
  <xsl:analyze-string select="." regex="(&#10;)">
    <xsl:matching-substring>
      <xsl:choose>
        <xsl:when test="regex-group(1)">&#8232;</xsl:when>                
      </xsl:choose>
    </xsl:matching-substring>
  </xsl:analyze-string>
</xsl:template>

但我得到的是:

<codeblock>&#8232;<bold/>&#8232;</codeblock>

我终于可以使用这个模板传递文本了:

<xsl:template match="codeblock//text()">
  <xsl:copy/>
</xsl:template>

成功!顺便说一下,我必须在代码块下的任何级别进行匹配,因此它也包括子 parmname 元素的文本。由于我能够使用 <xsl:copy> 成功传递它,因此我尝试在替换换行符的同时传递文本:

<xsl:template match="codeblock//text()">
  <xsl:copy>
    <xsl:analyze-string select="." regex="(&#10;)">
      <xsl:matching-substring>
        <xsl:choose>
          <xsl:when test="regex-group(1)">&#8232;</xsl:when>                
        </xsl:choose>
      </xsl:matching-substring>
    </xsl:analyze-string>
  </xsl:copy>
</xsl:template>

但现在它不会取代新的换行符。相反,我得到了这个(这是我希望在没有定义任何模板的情况下得到的):

<codeblock>This is the first line of my code block.
This is my second line to prove that line feeds are preserved.
This line proves that <bold>child elements</bold> are passed through.</codeblock>

我知道这是一个很长而且有点令人费解的问题。我只是觉得如果我能首先解决为什么它不传递文本的问题,剩下的就相当简单了。很抱歉,我无法提供我的来源 XML 或 XSL,因为它处于 NDA 之下,但如果您需要更多,请告诉我,我会尽力提供。 (我的 XSL 样式表由 12 个不同的文件组成,所以我无法提供所有文件,即使是通用化的。)

关于我可能在我的样式表中寻找的内容的任何建议,这些建议可以解释为什么文本会出现,或者任何关于如何强制它通过的建议,就像我对 <xsl:copy> 所做的那样,同时仍然替换换行符将非常感谢

编辑添加: 我想到它没有进行替换的原因是它看起来实际上不是换行符。它更像是代码中的新行,而不是文本中的换行符(或硬 return)。我想我可能需要在每行末尾插入 &#8232; 字符时规范化文本。仍在调查中,但欢迎提出建议!

编辑更新: 感谢 post How to detect line breaks in XSLT,我已经接近了,但仍然不是我需要的地方。使用此代码,我能够检测 XML 中的换行符并为 InDesign 插入换行符:

<xsl:template match="codeblock//text()">
  <xsl:for-each select="tokenize(., '\n?')[.]">
    <xsl:sequence select="."/>
    <xsl:text>&#8232;</xsl:text>
  </xsl:for-each>
</xsl:template>

但是,它还会在字符串末尾插入换行符,即使它不是行尾也是如此。例如,我现在得到:

<codeblock>This is the first line of my code block.&#8232;This is my second line to prove that line feeds are preserved.&#8232;This line proves that &#8232;<bold>child elements&#8232;</bold> are passed through.&#8232;</codeblock>

我不想在 'bold' 开始和结束标记或 codeblock 结束标记前使用换行符。我只是想让它出现在有实际换行的地方。我尝试替换 \r 但只是忽略了新行并将其放在标签前面。有谁知道另一个可以在这里使用的转义字符?

一个非常长的问题 - 但仍然不清楚你到底在问什么(也没有可重现的例子)。

如果 - 看起来 - 你想在 codeblock 元素下的所有文本节点中用行分隔符替换换行符,你应该能够简单地做到:

<xsl:template match="codeblock//text()">
    <xsl:value-of select="translate(., '&#10;', '&#8232;')" />
</xsl:template>

如果这不起作用,则要么您有一个覆盖模板,要么文本不包含换行符。您可以通过更改模板来测试第一种情况:

<xsl:template match="codeblock//text()">BINGO</xsl:template>

并观察结果是否所有目标文本节点都更改为“BINGO”。要测试第二种情况,您可以使用 string-to-codepoints() 函数逐个字符地分析文本。

您的模板缺少 xsl:non-matching-substring 来处理文本节点的不匹配部分。

<xsl:template match="codeblock//text()">
  <xsl:analyze-string select="." regex="\n">
    <xsl:matching-substring>
      <xsl:text>&#8232;</xsl:text>                
    </xsl:matching-substring>
    <xsl:non-matching-substring>
      <xsl:value-of select="."/>
    </xsl:non-matching-substring>
  </xsl:analyze-string>
</xsl:template>

但是, 更简单,因为您不需要 xsl:analyze-string 来替换所有子字符串。