0

使用 xslt3 (Saxon JS) 并且不绑定到特定的 XSLT 版本

鉴于此 sample.xml

<?xml version=”1.0″ encoding=”UTF-8″?>
<tasks>
    <task id="id0"/>
    <task id="id1" parent="#id0" deps="#id2 #id3 #id0"/>
    <task id="id2" parent="#id0"/>
    <task id="id3" parent="#id0"/>
</tasks>

这个sample.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:for-each select="/tasks/task">
            <xsl:variable name="parentTask" select="replace(@parent,'#','')"/>
            
            <xsl:variable name="depTasks">
                <xsl:choose>
                    <xsl:when test="@deps">

                        <xsl:value-of select="tokenize(replace(@deps,'#',''),'\s')"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="$parentTask"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:variable>

            <parent id="{@id}">
                <xsl:for-each select="/tasks/task[@id=$depTasks]">
                    <dep>
                        <xsl:value-of select="@id"/>
                    </dep>
                </xsl:for-each>
            </parent>

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

我希望$depTasks变量包含一个数组(属性中列出的所有depdeps)或单个值(属性中列出的depparent),并获得如下转换:

<?xml version="1.0" encoding="UTF-8"?>
<parent id="id0"/>
<parent id="id1">
    <dep>id2</dep>
    <dep>id3</dep>
    <dep>id0</dep>
</parent>
<parent id="id2">
    <dep>id0</dep>
</parent>
<parent id="id3">
    <dep>id0</dep>
</parent>

但是相反,我得到了这个结果(似乎只分配了标记化的第一个元素)

<?xml version="1.0" encoding="UTF-8"?>
<parent id="id0"/>
<parent id="id1">
    <dep>id2</dep>
    <!-- MISSING DEPS -->
</parent>
<parent id="id2">
    <dep>id0</dep>
</parent>
<parent id="id3">
    <dep>id0</dep>
</parent>

条件内部变量分配是否存在问题或限制?或者我在这里错过了什么?

请注意,如果我稍微更改样式表并将变量 OUTSIDE 声明为条件,则会按预期分配一个数组。

样本2.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:for-each select="/tasks/task">
            <xsl:variable name="parentTask" select="replace(@parent,'#','')"/>
            <!-- VARIABLE assignement outside a condition: value is an array -->
            <xsl:variable name="depTasks" select="tokenize(replace(@deps,'#',''),'\s')"/>

            <parent id="{@id}">
                <xsl:for-each select="/tasks/task[@id=$depTasks]">
                    <dep>
                        <xsl:value-of select="@id"/>
                    </dep>
                </xsl:for-each>
            </parent>

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

导致这种转变

<?xml version="1.0" encoding="UTF-8"?>
<parent id="id0"/>
<parent id="id1">
    <dep>id0</dep>
    <dep>id2</dep>
    <dep>id3</dep>
</parent>
<parent id="id2"/>
<parent id="id3"/>
4

2 回答 2

2

除了 Martin Honnen 的回答:

如果您使用的是 XSLT 3.0,请不要将样式表声明为version="1.0". 这样做会调用向后兼容的行为,这可能会显着改变您的结果。

例如,xsl:value指令(您在多个地方使用)将仅输出所选序列的第一个节点的字符串值,而不是全部。

于 2022-01-12T11:35:41.443 回答
1

您根本没有在代码中使用任何数组。在

      <xsl:variable name="depTasks">
            <xsl:choose>
                <xsl:when test="@deps">

                    <xsl:value-of select="tokenize(replace(@deps,'#',''),'\s')"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$parentTask"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

tokenize您正在创建一个结果树片段,其中包含一个带有调用或$parentTask变量的字符串值的文本节点。

<xsl:variable name="depTasks" select="tokenize(replace(@deps,'#',''),'\s')"/>变量的值是调用的结果,它tokenize是一个字符串值序列。

如果对于前面的方法,虽然不推荐,但你想要一个字符串序列,你需要使用

<xsl:variable name="depTasks" as="xs:string*">
  ...
  <xsl:sequence select="tokenize(...)"/>

  ...

</xsl:variable>

更复杂部分的首选方法是select="if (@deps) then tokenize(...) else $parentTask".

于 2022-01-12T10:39:06.020 回答