XSLT:如何保留元素之间的空白?

XSLT: How to preserve whitespace between elements?

我有一个新要求,要使转换后的 XML 更具可读性,即在元素之间保留 cr、制表符和其他白色 space。

我似乎不知道如何保存白色space。

有人可以帮忙吗?

XML 文件

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
    </Fragment>
</Wix>

XSL 文件:

  <?xml version="1.0" encoding="UTF-8"?>
  <xsl:stylesheet version="2.0" 
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    xmlns:m="http://schemas.microsoft.com/wix/2006/wi">
  <xsl:preserve-space elements="*" />
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="/m:Wix">
    <xsl:message>Matched Wix</xsl:message>
    <xsl:copy>
      <!-- Insert the new include processing instruction -->
      <xsl:processing-instruction name="include">
        <xsl:text>$(sys.CURRENTDIR)src/includes/globals.wxi </xsl:text>
      </xsl:processing-instruction>
      <!-- place the existing children into the output -->
      <xsl:apply-templates select="@* | *"/> 
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

当前输出:

<?xml version="1.0" encoding="UTF-8"?><Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"><?include $(sys.CURRENTDIR)src/includes\globals.wxi ?><Fragment>
    </Fragment></Wix>

期望输出

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <?include $(sys.CURRENTDIR)src/includes\globals.wxi ?>
    <Fragment>
    </Fragment>
</Wix>

考虑使用换行符 &#xa; 和制表符实体 &#9;,方法是在流程指令前后添加以下文本。并确保将缩进输出 header 添加到顶部:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                  xmlns:m="http://schemas.microsoft.com/wix/2006/wi">
  <xsl:output version="1.0" encoding="UTF-8" indent="yes" />
  <xsl:preserve-space elements="*" />

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="/m:Wix">
    <xsl:message>Matched Wix</xsl:message>
    <xsl:copy>
      <xsl:text>&#xa; &#9;</xsl:text>      
      <!-- Insert the new include processing instruction -->
      <xsl:processing-instruction name="include">
        <xsl:text>$(sys.CURRENTDIR)src/includes/globals.wxi </xsl:text>
      </xsl:processing-instruction>
      <xsl:text>&#xa; &#9;</xsl:text>      
      <!-- place the existing children into the output -->
        <xsl:apply-templates select="@* | *"/>        
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

或者,使用多个 non-break space 实体 &#160; 代替制表符以实现更精确的对齐:

<xsl:text>&#xa;&#160;&#160;&#160;&#160;</xsl:text>    

输出

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <?include $(sys.CURRENTDIR)src/includes/globals.wxi?>
    <Fragment>
    </Fragment>
</Wix>

我选择的漂亮打印工具是 xmlint。

xmllint --format old.xml > new.xml

但我确实看到您正在添加处理指令。所以需要xslt。

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:m="http://schemas.microsoft.com/wix/2006/wi"
    exclude-result-prefixes="m">

    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:preserve-space elements="*" />

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

    <xsl:template match="m:Wix">
        <xsl:message>Matched Wix</xsl:message>
        <Wix>

            <xsl:call-template name="CR"/>
            <xsl:call-template name="TAB"/>

            <!-- Insert the new include processing instruction -->
            <xsl:processing-instruction name="include">
                <xsl:text>$(sys.CURRENTDIR)src/includes/globals.wxi</xsl:text>
            </xsl:processing-instruction>

            <!-- place the existing children into the output -->
            <xsl:apply-templates/> 
        </Wix>
    </xsl:template>

    <xsl:template match="m:Fragment">
        <Fragment>
            <xsl:apply-templates/>
        </Fragment>
    </xsl:template>

    <xsl:template name="CR">
        <xsl:text>&#xa;</xsl:text>      
    </xsl:template>

    <xsl:template name="TAB">
        <xsl:text>&#9;</xsl:text>
    </xsl:template>

</xsl:stylesheet>

如果您的真实 XML 变得更复杂,您可能需要先进行 xmllint。然后做一个简单的xslt添加处理指令。 Xmllint 是漂亮的空白感知,xslt 你必须把它烤进去。

您的输入中有三个空白文本节点:两个是 Fragment 元素的同级元素,一个是 Fragment 元素的子元素。

前两个不会复制到您的输出中,因为您的 m:Wix 元素模板会忽略它们:它 <xsl:apply-templates select="@* | *"/> 只选择元素子元素,而不选择文本节点子元素。

Fragment 的空白文本内容已处理,并保留在您的输出中。

现在:你在问题中说了两件事:(a) 你想让输出可读,(b) 你想保留输入中的空白。我建议 (b) 不是实现 (a) 的最佳方式。实现 (a) 的最佳方法是忽略输入中存在的空格,并使用 xsl:output indent="yes" 在输出中添加新的空格。

但是,如果您确实想将空格从输入复制到输出,则在处理元素的子元素时需要使用 select="node()" 而不是 select="*"