我在将输入树的一部分分组到容器元素中并使其他部分保持完整时遇到了一些问题。我正在尝试将其for-each-group
用作练习。
逻辑:
w
元素的元素。如果有其他内容,则继续“常规”处理,否则继续此序列中的下一步。container
使用当前节点的内容构建一个元素,然后尝试将以下不包含w
元素的相邻同级项拉入container
。step
带有w
元素的A应该在容器外部。作为单独的元素(如果有w
和其他元素),或者作为新的容器(如果只有w
子元素)。输入示例(示例中的body
元素可以看作是更大树的片段):
<?xml version="1.0" encoding="UTF-8"?>
<body>
<step>
<p>step 1</p>
</step>
<step>
<p>step 2</p>
</step>
<step>
<w>Warning A</w>
<p>step 3</p>
</step>
<step>
<p>step 4</p>
</step>
<step>
<p>step 5</p>
</step>
<step>
<w>Spec Warning X</w>
<w>Spec Warning Y</w>
</step>
<step>
<p>step 6</p>
</step>
<step>
<p>step 7</p>
</step>
<step>
<p>step 8</p>
</step>
<step>
<p>step 9</p>
</step>
<step>
<p>step 10</p>
</step>
<step>
<p>step 11</p>
</step>
<step>
<w>Warning B</w>
<p>step 12</p>
</step>
<step>
<p>step 13</p>
</step>
<step>
<p>step 14</p>
</step>
</body>
所需的输出:
<?xml version="1.0" encoding="UTF-8"?>
<body>
<step>
<p>step 1</p>
</step>
<step>
<p>step 2</p>
</step>
<step>
<w>Warning A</w>
<p>step 3</p>
</step>
<step>
<p>step 4</p>
</step>
<step>
<p>step 5</p>
</step>
<container>
<w>Spec Warning X</w>
<w>Spec Warning Y</w>
<step>
<p>step 6</p>
</step>
<step>
<p>step 7</p>
</step>
<step>
<p>step 8</p>
</step>
<step>
<p>step 9</p>
</step>
<step>
<p>step 10</p>
</step>
<step>
<p>step 11</p>
</step>
</container>
<step>
<w>Warning B</w>
<p>step 12</p>
</step>
<step>
<p>step 13</p>
</step>
<step>
<p>step 14</p>
</step>
</body>
初步测试:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<xsl:element name="body">
<xsl:apply-templates select="*"/>
</xsl:element>
</xsl:template>
<xsl:template match="step[w and not(p)]">
<xsl:element name="container">
<xsl:apply-templates/>
<xsl:for-each-group select="following-sibling::*" group-adjacent="self::step[not(w)]">
<xsl:copy-of select="current-group()"/>
</xsl:for-each-group>
</xsl:element>
</xsl:template>
<xsl:template match="step[p]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="w">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="step[p and not(w)][preceding-sibling::step[w][1][not(p)]]"/>
</xsl:transform>
结果(http://xsltransform.net/eixk6Sw/2):
<body>
<step>
<p>step 1</p>
</step>
<step>
<p>step 2</p>
</step>
<step>
<w>Warning A</w>
<p>step 3</p>
</step>
<step>
<p>step 4</p>
</step>
<step>
<p>step 5</p>
</step>
<container>
<w>Spec Warning X</w>
<w>Spec Warning Y</w>
<step>
<p>step 6</p>
</step>
<step>
<p>step 7</p>
</step>
<step>
<p>step 8</p>
</step>
<step>
<p>step 9</p>
</step>
<step>
<p>step 10</p>
</step>Error on line 14
XTTE1100: An empty sequence is not allowed as the @group-adjacent attribute of xsl:for-each-group
in built-in template rule
at xsl:apply-templates (#7)
processing /body
我当前的问题是我看不到如何使用分组技术,而是将处理限制在第一组(也就是我的上下文节点后面的组),而不是处理所有组。
第二次尝试:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<body>
<xsl:apply-templates select="*"/>
</body>
</xsl:template>
<xsl:template match="step[w and not(p)]"> <!-- Find a step with w elements only. -->
<xsl:element name="container">
<xsl:apply-templates/> <!-- Get content from current node. -->
<!-- This where it gets dicey and I'm guessing a lot -->
<!-- Get all following adjacent elements in groups, where the interesting group is
the first one containing step elements with no w elements.
So group element that doesn's include a w element.-->
<xsl:for-each-group select="following-sibling::*" group-adjacent="boolean(self::step[not(w)])">
<!-- Check if the group actually is according to the criteria. The group can include other nodes as well? -->
<!-- And also check if the first preceding step with a w element also lacks any p elements.
If so, this has to be the first group. -->
<xsl:if test="current-grouping-key() and preceding-sibling::step[w][1][not(p)]">
<xsl:sequence select="current-group()"/>
</xsl:if>
</xsl:for-each-group>
</xsl:element>
</xsl:template>
<xsl:template match="step[w and p] | step[p][not(preceding-sibling::step[w][1][not(p)])]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="w ">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="step[p and not(w)][preceding-sibling::step[w][1][not(p)]]"/>
</xsl:transform>
我知道我可以通过仅使用w元素找到我的步骤来使它起作用,然后在一个特殊的模式下应用模板来处理下一步的同级操作,并让该模板在不使用w元素的情况下提取下一个同级。向前。这可以按预期工作,但是我想学习其他技术:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<xsl:element name="body">
<xsl:apply-templates select="*"/>
</xsl:element>
</xsl:template>
<xsl:template match="step[w and not(p)]">
<xsl:element name="container">
<xsl:apply-templates/>
<xsl:apply-templates select="following-sibling::*[1][self::step[p and not(w)]]" mode="keep"/>
</xsl:element>
</xsl:template>
<xsl:template match="step[p]" mode="keep">
<xsl:copy-of select="."/>
<xsl:apply-templates select="following-sibling::*[1][self::step[p and not(w)]]" mode="keep"/>
</xsl:template>
<xsl:template match="step[p]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="w">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="step[p and not(w)][preceding-sibling::step[w][1][not(p)]]"/>
</xsl:transform>
我的第二次尝试似乎给了我想要的结果,但这来自反复试验,并从结果中得到了一些免费的解释...
随意评论我的方法和问题。
使用时for-each-group
,我倾向于在父级模板(例如body
)中使用它,并使用项(例如step
s)作为总体。我不确定我是否已完全理解单个样本的要求,但是假设我们可以重新制定第二个要求,因为尝试查找具有w
嵌套分组的第一个项目可能会起作用:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="body">
<xsl:copy>
<xsl:for-each-group select="step" group-starting-with="step[w and not(p)]">
<xsl:choose>
<xsl:when test="w and not(p)">
<xsl:variable name="wrapper" select="."/>
<xsl:for-each-group select="tail(current-group())" group-ending-with="step[w]">
<xsl:choose>
<xsl:when test="position() = 1">
<container>
<xsl:apply-templates select="$wrapper, current-group()[position() lt last()]"/>
</container>
<xsl:apply-templates select="current-group()[last()]"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
外层xsl:for-each-group select="step" group-starting-with="step[w and not(p)]"
应该标识你的container
元素,因为与往常一样,group-starting-with
你可以得到一个不是由模式形成的组,而是在内部,只有在我们有一个想要的step
组之一的情况下才需要包装,因此我们必须重新检查条件test="w and not(p)"
。
然后在内部,使用第二个分组来标识要包装的项目的“末端”:xsl:for-each-group select="tail(current-group())" group-ending-with="step[w]"
,它基本上使我们可以选择step
不具有的相邻s w
。我们只想包装第一个这样的序列或组,因此xsl:when test="position() = 1"
使用。
所有xsl:otherwise
分支机构都将收集到的所有信息推送到身份转换中。
与往常一样,感谢您的快速回复!我有一些问题:第一个for-each-group返回一个序列(就像所有的for-each-group一样)?那么测试在做什么呢?我了解在开始第一个组之前需要进行测试以处理所有初始元素吗?嵌套分组负责每个容器,在这里pos-test创建容器,其余位置填充所创建的容器?为了充分理解它,我将开始调试,但是您能否对您的解决方案发表一点评论?也许可以解释我的分组失败的地方?