使用 XSLT 将空格保留在根元素之外

Keeping whitespace outside of root element with XSLT

我正在使用 XSLT 处理 XML 受源代码控制且必须保持人类可读的文档。

因此,我尝试编辑一个文本节点,但保持文档的其余部分完全原样。

我发现根元素外的空白被删除了。特别是 XML 声明和根元素的打开标记之间的换行符,以及文档末尾的换行符。

当我将 indent=yes 属性添加到 xsl:output 元素时,换行符确实出现在文档末尾。但是,XML 声明和根元素的开放标记之间的换行符仍然缺失。

我的样式表缺少什么?

XSLT

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

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

    <xsl:param name="newVersion"/>
    <xsl:template match="/project/version/text()">
        <xsl:value-of select="$newVersion" />
    </xsl:template>
</xsl:stylesheet>

来源XML

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.nuance</groupId>
        <artifactId>parent-test-repo</artifactId>
        <version>0.1.0</version>
    </parent>

    <artifactId>test-repo-1</artifactId>
    <name>test-repo-1</name>
    <version>1.1.0</version>
</project>

预期结果 — 只有 /project/version 中的文本已更改

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.nuance</groupId>
        <artifactId>parent-test-repo</artifactId>
        <version>0.1.0</version>
    </parent>

    <artifactId>test-repo-1</artifactId>
    <name>test-repo-1</name>
    <version>2.0.0</version>
</project>

实际结果

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.nuance</groupId>
        <artifactId>parent-test-repo</artifactId>
        <version>0.1.0</version>
    </parent>

    <artifactId>test-repo-1</artifactId>
    <name>test-repo-1</name>
    <version>2.0.0</version>
</project>

如果您使用的是Java,请尝试添加以下行:

transformer.setOutputProperty("http://www.oracle.com/xml/is-standalone", "yes");

虽然 XML 规范允许白色 space 文本作为根元素前后的杂项,但大多数抽象模型(如信息集)和树模型(如 Xdm)不包含此类白色 space文本作为模型中的节点。

因此,就 XSLT 处理器而言,标记 <?xml version="1.0"?><root/><?xml version="1.0"?> <root/> 生成同一棵树,其文档节点包含单个元素节点作为子节点。只有根元素之外的注释和处理指令才会显示在 Xdm 树中。

所以您的样式表没有做错任何事情,您对保留这种白色 space 文本节点的期望根本不符合规范和您使用的实现。

至于输出,序列化,我想其他 XSLT 处理器可能会在 XML 声明和结果树序列化的根元素之间输出一个换行符。

看看这是否能产生可接受的结果:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://maven.apache.org/POM/4.0.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:preserve-space elements="project"/>

<xsl:param name="newVersion"/>

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

<xsl:template match="/project/version/text()">
    <xsl:value-of select="$newVersion" />
</xsl:template>

</xsl:stylesheet>

演示https://xsltfiddle.liberty-development.net/eieFA1D