如何使用 XSLT 获取所有可能的节点组合?
How to get all possible node combinations with XSLT?
https://xsltfiddle.liberty-development.net/bFWR5DY/1
我的目标是将每个 person.type
节点相互相乘:
<?xml version="1.0" encoding="utf-8" ?>
<persons>
<department>OUTER</department>
<person>
<name>john</name>
<types>
<type code="A"/>
<type code="B"/>
</types>
</person>
<person>
<name>doe</name>
<types>
<type code="A"/>
<type code="C"/>
</types>
</person>
<person>
<name>jane</name>
<types>
<type code="D"/>
<type code="X"/>
</types>
</person>
</persons>
我开始如下提取值:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:mode on-no-match="shallow-skip"/>
<xsl:template match="persons">
<xsl:for-each select="person">
<xsl:for-each select="type">
<xsl:value-of select=".//@code" separator=";"/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
结果:
A
B
A
C
D
X
想要的结果:
OUTER;john;A;doe;A;jane;D
OUTER;john;A;doe;A;jane;X
OUTER;john;A;doe;c;jane;D
OUTER;john;A;doe;C;jane;X
OUTER;john;B;doe;A;jane;D
OUTER;john;B;doe;A;jane;X
OUTER;john;B;doe;C;jane;D
OUTER;john;B;doe;C;jane;X
我怎样才能到达那里?
执行此操作的一种方法是使用递归模板。
首先匹配第一个 person
元素下的 type
元素,然后递归调用模板下一个 person
下的 type
元素,传递 "running total" 和你一样。
试试这个 XSLT
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:mode on-no-match="shallow-skip"/>
<xsl:param name="quote">"</xsl:param>
<xsl:template match="persons">
<xsl:apply-templates select="person[1]/types/type">
<xsl:with-param name="runningTotal" select="department" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="type">
<xsl:param name="runningTotal" />
<xsl:variable name="newRunningTotal" select="concat($runningTotal, ';', ../../name, ':', @code)" />
<xsl:variable name="nextPerson" select="../../following-sibling::person[1]" />
<xsl:choose>
<xsl:when test="$nextPerson">
<xsl:apply-templates select="$nextPerson/types/type">
<xsl:with-param name="runningTotal" select="$newRunningTotal" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newRunningTotal" />
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
由于 Tim 给我施加了负担和压力,试图找到一些 XSLT 3 compact/complex 方式,我尝试使用累加器和 XPath:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" indent="no"/>
<xsl:mode on-no-match="shallow-skip" use-accumulators="#all"/>
<xsl:accumulator name="person-codes" as="xs:string*" initial-value="()">
<xsl:accumulator-rule match="persons/person" select="()"/>
<xsl:accumulator-rule match="persons/person/types/type" select="$value, string(@code)"/>
</xsl:accumulator>
<xsl:accumulator name="permutations" as="xs:string*" initial-value="()">
<xsl:accumulator-rule phase="end" match="person"
select="
if (empty($value))
then
accumulator-after('person-codes')
else
for $v in $value,
$cv in accumulator-after('person-codes')
return $v || $cv"/>
</xsl:accumulator>
<xsl:template match="persons">
<xsl:apply-templates/>
<xsl:value-of select="accumulator-after('permutations')" separator=" "/>
</xsl:template>
</xsl:stylesheet>
这似乎为 Saxon 9.8 和 9.9 提供了正确的输出,并在 https://xsltfiddle.liberty-development.net/gWEamL3/4 提供了最新输入,但没有考虑要收集的各种其他数据,只有 code
属性。
基于累加器的方法甚至适用于 Saxon EE 和流:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" indent="no"/>
<xsl:mode streamable="yes" on-no-match="shallow-skip" use-accumulators="#all"/>
<xsl:accumulator name="person-codes" as="xs:string*" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="persons/person" select="()"/>
<xsl:accumulator-rule match="persons/person/types/type" select="$value, string(@code)"/>
</xsl:accumulator>
<xsl:accumulator name="permutations" as="xs:string*" initial-value="()" streamable="yes">
<xsl:accumulator-rule phase="end" match="person"
select="
if (empty($value))
then
accumulator-after('person-codes')
else
for $v in $value,
$cv in accumulator-after('person-codes')
return
$v || $cv"
/>
</xsl:accumulator>
<xsl:template match="persons">
<xsl:apply-templates/>
<xsl:value-of select="accumulator-after('permutations')" separator=" "/>
</xsl:template>
</xsl:stylesheet>
一个也可以捕获部门和名称并且应该在没有流式传输(例如 Saxon 9.8 或更高版本 HE 或 PE)或流式传输(例如 Saxon 9.8 或更高版本 EE)的情况下工作的版本是:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:param name="sep" as="xs:string" select="';'"/>
<xsl:output method="text" indent="no"/>
<xsl:mode on-no-match="shallow-skip" streamable="yes" use-accumulators="#all"/>
<xsl:accumulator name="department" as="xs:string?" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="persons/department/text()" select="string()"/>
</xsl:accumulator>
<xsl:accumulator name="person-name" as="xs:string?" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="person" select="()"/>
<xsl:accumulator-rule match="person/name/text()" select="string()"/>
</xsl:accumulator>
<xsl:accumulator name="person-codes" as="xs:string*" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="persons/person" select="()"/>
<xsl:accumulator-rule match="persons/person/types/type"
select="$value, accumulator-before('person-name') || $sep || string(@code)"/>
</xsl:accumulator>
<xsl:accumulator name="permutations" as="xs:string*" initial-value="()" streamable="yes">
<xsl:accumulator-rule phase="end" match="person"
select="
if (empty($value))
then
for $iv in accumulator-after('person-codes')
return
accumulator-before('department') || $sep || $iv
else
for $v in $value,
$cv in accumulator-after('person-codes')
return
$v || $sep || $cv"
/>
</xsl:accumulator>
<xsl:template match="persons">
<xsl:apply-templates/>
<xsl:value-of select="accumulator-after('permutations')" separator=" "/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bFWR5DY/1
我的目标是将每个 person.type
节点相互相乘:
<?xml version="1.0" encoding="utf-8" ?>
<persons>
<department>OUTER</department>
<person>
<name>john</name>
<types>
<type code="A"/>
<type code="B"/>
</types>
</person>
<person>
<name>doe</name>
<types>
<type code="A"/>
<type code="C"/>
</types>
</person>
<person>
<name>jane</name>
<types>
<type code="D"/>
<type code="X"/>
</types>
</person>
</persons>
我开始如下提取值:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:mode on-no-match="shallow-skip"/>
<xsl:template match="persons">
<xsl:for-each select="person">
<xsl:for-each select="type">
<xsl:value-of select=".//@code" separator=";"/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
结果:
A
B
A
C
D
X
想要的结果:
OUTER;john;A;doe;A;jane;D
OUTER;john;A;doe;A;jane;X
OUTER;john;A;doe;c;jane;D
OUTER;john;A;doe;C;jane;X
OUTER;john;B;doe;A;jane;D
OUTER;john;B;doe;A;jane;X
OUTER;john;B;doe;C;jane;D
OUTER;john;B;doe;C;jane;X
我怎样才能到达那里?
执行此操作的一种方法是使用递归模板。
首先匹配第一个 person
元素下的 type
元素,然后递归调用模板下一个 person
下的 type
元素,传递 "running total" 和你一样。
试试这个 XSLT
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:mode on-no-match="shallow-skip"/>
<xsl:param name="quote">"</xsl:param>
<xsl:template match="persons">
<xsl:apply-templates select="person[1]/types/type">
<xsl:with-param name="runningTotal" select="department" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="type">
<xsl:param name="runningTotal" />
<xsl:variable name="newRunningTotal" select="concat($runningTotal, ';', ../../name, ':', @code)" />
<xsl:variable name="nextPerson" select="../../following-sibling::person[1]" />
<xsl:choose>
<xsl:when test="$nextPerson">
<xsl:apply-templates select="$nextPerson/types/type">
<xsl:with-param name="runningTotal" select="$newRunningTotal" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newRunningTotal" />
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
由于 Tim 给我施加了负担和压力,试图找到一些 XSLT 3 compact/complex 方式,我尝试使用累加器和 XPath:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" indent="no"/>
<xsl:mode on-no-match="shallow-skip" use-accumulators="#all"/>
<xsl:accumulator name="person-codes" as="xs:string*" initial-value="()">
<xsl:accumulator-rule match="persons/person" select="()"/>
<xsl:accumulator-rule match="persons/person/types/type" select="$value, string(@code)"/>
</xsl:accumulator>
<xsl:accumulator name="permutations" as="xs:string*" initial-value="()">
<xsl:accumulator-rule phase="end" match="person"
select="
if (empty($value))
then
accumulator-after('person-codes')
else
for $v in $value,
$cv in accumulator-after('person-codes')
return $v || $cv"/>
</xsl:accumulator>
<xsl:template match="persons">
<xsl:apply-templates/>
<xsl:value-of select="accumulator-after('permutations')" separator=" "/>
</xsl:template>
</xsl:stylesheet>
这似乎为 Saxon 9.8 和 9.9 提供了正确的输出,并在 https://xsltfiddle.liberty-development.net/gWEamL3/4 提供了最新输入,但没有考虑要收集的各种其他数据,只有 code
属性。
基于累加器的方法甚至适用于 Saxon EE 和流:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" indent="no"/>
<xsl:mode streamable="yes" on-no-match="shallow-skip" use-accumulators="#all"/>
<xsl:accumulator name="person-codes" as="xs:string*" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="persons/person" select="()"/>
<xsl:accumulator-rule match="persons/person/types/type" select="$value, string(@code)"/>
</xsl:accumulator>
<xsl:accumulator name="permutations" as="xs:string*" initial-value="()" streamable="yes">
<xsl:accumulator-rule phase="end" match="person"
select="
if (empty($value))
then
accumulator-after('person-codes')
else
for $v in $value,
$cv in accumulator-after('person-codes')
return
$v || $cv"
/>
</xsl:accumulator>
<xsl:template match="persons">
<xsl:apply-templates/>
<xsl:value-of select="accumulator-after('permutations')" separator=" "/>
</xsl:template>
</xsl:stylesheet>
一个也可以捕获部门和名称并且应该在没有流式传输(例如 Saxon 9.8 或更高版本 HE 或 PE)或流式传输(例如 Saxon 9.8 或更高版本 EE)的情况下工作的版本是:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:param name="sep" as="xs:string" select="';'"/>
<xsl:output method="text" indent="no"/>
<xsl:mode on-no-match="shallow-skip" streamable="yes" use-accumulators="#all"/>
<xsl:accumulator name="department" as="xs:string?" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="persons/department/text()" select="string()"/>
</xsl:accumulator>
<xsl:accumulator name="person-name" as="xs:string?" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="person" select="()"/>
<xsl:accumulator-rule match="person/name/text()" select="string()"/>
</xsl:accumulator>
<xsl:accumulator name="person-codes" as="xs:string*" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="persons/person" select="()"/>
<xsl:accumulator-rule match="persons/person/types/type"
select="$value, accumulator-before('person-name') || $sep || string(@code)"/>
</xsl:accumulator>
<xsl:accumulator name="permutations" as="xs:string*" initial-value="()" streamable="yes">
<xsl:accumulator-rule phase="end" match="person"
select="
if (empty($value))
then
for $iv in accumulator-after('person-codes')
return
accumulator-before('department') || $sep || $iv
else
for $v in $value,
$cv in accumulator-after('person-codes')
return
$v || $sep || $cv"
/>
</xsl:accumulator>
<xsl:template match="persons">
<xsl:apply-templates/>
<xsl:value-of select="accumulator-after('permutations')" separator=" "/>
</xsl:template>
</xsl:stylesheet>