【问题标题】:xsl:preserve-space breaks the rest of the codexsl:preserve-space 破坏了其余代码
【发布时间】:2020-01-27 22:59:27
【问题描述】:

我的团队使用 XML 编辑器 MadCap Flare 来编写包含 PDF 和 HTML 输出的技术文档。我们需要对某些页面使用 H2 以正确格式化 PDF,但出于 SEO 的目的,我们需要将这些转换为用于 Web 的 H1。我编写了一个构建事件,可在网络发布时将 H2 转换为 H1。但是,我刚刚意识到 XSL 代码错误地去除了变量和图像之间的空格。我发现了 xsl:preserve-space,但使用它会破坏其余代码,因此 H2 永远不会转换为 H1。我需要找到一种方法来执行转换保留空间。

这是 source HTM 的 sn-p (在你问之前,不,我不能删除 span 标签;它们是由 Flare 在将变量转换为文本时插入的):

                <div role="main" id="mc-main-content">
                    <h2><span class="GlobalCompany">BeyondTrust</span> <span class="ProductsPA">Privileged Remote Access</span> Web Rep Console Requirements</h2>
                    <p>To run the <span class="GlobalCompany">BeyondTrust</span> <span class="ProductNamesWebConsole">web rep console</span> on your system...</p>

这是我用作构建事件的批处理文件

@ECHO Off

set outputDir=%1
@set XSLAltova=C:\Users\%username%\AltovaXML.exe

REM Create filelist
dir %outputDir%*.htm /b /s /A-D > file_list.txt
@echo ^<filelist^>^</filelist^> > pre_filelist.xml

REM XML-ize filelist
%XSLAltova% /xslt2 convert_filelist.xsl /in pre_filelist.xml  /out pre_list.xml

REM Replace starting h2 tags with h1 tags
%XSLAltova% /xslt2 h2toh1.xsl /in pre_list.xml  /out null.xml

REM Garbage collection
DEL pre_list.xml
DEL pre_filelist.xml
DEL file_list.txt

这里是 convert_filelist.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!-- Set output style. XML with no indentations -->
    <xsl:output indent="no" method="xml" omit-xml-declaration="yes"/>

<!-- Reads the file list text file into memory as a global variable. -->
    <xsl:variable name="fileList">file_list.txt</xsl:variable>  

<!-- Parses the file list text file to create an XML list of files that can be fed to the transformer -->
    <xsl:template match="filelist">
    <!-- Create a variable that can be parsed -->
        <xsl:variable name="filelist_raw"><xsl:value-of select="unparsed-text($fileList,'UTF-8')"/></xsl:variable>
    <!-- Create a open and close file tags for each line in the list -->
        <xsl:variable name="driveLetter"><xsl:value-of select="substring-before(unparsed-text($fileList,'UTF-8'),':')"/>:<xsl:text disable-output-escaping="yes">\\</xsl:text></xsl:variable>
        <xsl:variable name="driveLetterReplacement"><xsl:text disable-output-escaping="yes">&lt;file&gt;</xsl:text><xsl:value-of select="$driveLetter"/></xsl:variable>
    <!-- Generate an xml tree. The value-of is doing a text-level replacement. Looking for the drive letter and replacing it  -->
    <!-- with the file open tag and drive letter. Looking for the file extension and replacing with the extension and file close tag.-->
        <file_list><xsl:value-of select="replace(replace($filelist_raw,'.htm','.htm&lt;/file&gt;'),$driveLetter,$driveLetterReplacement)" disable-output-escaping="yes"/></file_list>
    </xsl:template>
</xsl:stylesheet>

这里是 h2toh1.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
    xmlns:MadCap="http://www.madcapsoftware.com/Schemas/MadCap.xsd" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!-- Set output style. -->
    <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
    <xsl:preserve-space elements="node()"/>

<!-- Begin traversing the list of files in the output folder. -->
    <xsl:template match="file_list">
        <xsl:for-each select="*">
            <xsl:variable name="filename" select="."/>
            <xsl:variable name="content" select="document($filename)"/>

<!-- Generate a new output file to replace the Flare generated file. Uses the same file name. Transparent to the end user. -->
            <xsl:result-document href="{$filename}" method="html">
                <xsl:apply-templates select="document($filename)">
                    <xsl:with-param name="content" select="$content"/>
                </xsl:apply-templates>
            </xsl:result-document>

        </xsl:for-each>
    </xsl:template>

<!-- Recreate each node as it appears in the generated document -->
    <xsl:template match="*">
        <xsl:param name="content"/>
        <xsl:variable name="name" select="name(.)"/>
        <xsl:element name="{$name}">
            <xsl:for-each select="@*">
                <xsl:copy-of select="."/>
            </xsl:for-each>
            <xsl:apply-templates/>
            </xsl:element>
    </xsl:template>

<!-- Select the first header and change it to an h1. -->
    <xsl:template match="*[matches(name(), 'h\d')][1]">
        <xsl:element name="h1">
            <xsl:for-each select="@*|node()">
                <xsl:copy-of select="."/>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

这是没有 xsl:preserve-space 的输出: (h1 style, missing spaces between variables)

这是使用 xsl:preserve-space 的输出: (h2 style, ugly blue for contrast, with spaces)

这是我想要但不能拥有的输出: (h1 style, with spaces)

就目前而言,我的网站有些损坏,而且我没有现成的方法来修复它而不撤消大量工作。任何帮助将不胜感激。

【问题讨论】:

  • 我希望&lt;xsl:preserve-space elements="node()"/&gt; 给出错误,因为elements 属性的属性值中应该有元素名称而不是节点测试。如果任务是将h2 元素转换为h1 元素,为什么匹配模式不使用match="h2",为什么要对任何元素*[matches(name(), 'h\d')][1] 进行奇怪的检查?如果您在问题中以 HTML 格式以及代码示例/sn-ps 的形式向我们展示结果,这将有所帮助,而不仅仅是渲染的屏幕截图。
  • 我使用*[matches(name(), 'h\d')][1] 的原因有两个。首先,我想说明任何滑入其中的 H3。其次,我只想替换第一个标题。我们的许多页面都有 H1,然后是 H2。在这种情况下,我不想更换 H2。这说明清楚了吗?
  • 另外,我使用&lt;xsl:preserve-space elements="node()" /&gt; 的原因也是双重的。首先,我显然误读了another topic。其次,使用星号并没有改变代码(可能是因为那些冗长的 span 标签),而使用 node() 做了我需要的。
  • 很难说,也许问 Altova 支持人员xsl:preserve-space elements="node()" 在他们的实现中做了什么,我只是希望它会产生一个错误。

标签: xml xslt


【解决方案1】:

你的xsl:preserve-space 声明破坏代码的原因是你给了它一个无效的值; elements 属性的值应该是元素名称列表或“*”。但无论如何它都不会有用。保留空白是默认设置,使用xsl:preserve-space 的唯一原因是为了抵消一般的xsl:strip-space

您似乎正在使用 Altova XSLT 处理器,我怀疑这意味着您正在使用 Microsoft MSXML 解析器,IIRC 在 XML 解析期间默认去除空白。如果是这种情况,那么您在样式表本身中所做的任何事情都不会影响这一点,因为空格在 XSLT 处理开始之前就已经消失了。但是,我不是这个产品组合方面的专家,所以我可能错了。

顺便说一句,这可能与您的问题无关,但是在将节点写入xsl:variable 时设置disable-output-escaping 的效果是高度不可预测的。多年来,W3C 工作组数次改变了主意,即这是否应该起作用(这被称为“粘性问题”)以及特定产品如何使用它是任何人的猜测。

【讨论】:

  • 我是 XSLT 的菜鸟(虽然在其他语言方面是老生常谈)。有没有办法选择我使用的处理器?是否有更适合此用例的特定处理器?
  • 作为 Saxon 处理器的开发者,我建议您使用 Saxon(我会这么说,不是吗?)
  • 我看到了! [恭敬地鞠躬] 这个软件我需要安装吗?代码中的调用?
  • 您最好先下载开源 Java 版本 Saxon-HE;它可以从命令行或通过 API 从 Java 应用程序调用。
猜你喜欢
  • 1970-01-01
  • 2018-11-19
  • 2017-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多