XSLT 1.0:从外部文档中提取顺序信息
XSLT 1.0: extract sequential infromation from external document
我正在尝试使用 xslt 执行 xml 到 xml 的转换,以便为每个元素指定一个 GUID,从外部 xml 文档中按顺序提取。
来源xml:
<?xml version="1.0"?>
<dataSets>
<data name="foo"/>
<data name="bar"/>
...
</dataSets>
ID 列表:
<?xml version="1.0"?>
<ids>
<id>some-GUID</id>
<id>another-GUID</id>
...
</ids>
期望的输出:
<?xml version="1.0"?>
<dataSets>
<data name="foo" id="some-GUID"/>
<data name="bar" id="another-GUID"/>
...
</dataSets>
但我每次都得到相同的第一个 ID:
<?xml version="1.0"?>
<dataSets>
<data name="foo" id="some-GUID"/>
<data name="bar" id="some-GUID"/>
...
</dataSets>
到目前为止,这是我得到的 xsl:
<?xml version="1.0"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/|node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="dataSets">
<xsl:for-each select="data">
<xsl:variable name="c">
<xsl:number value="count(preceding-sibling::*|self::*)"/>
</xsl:variable>
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="document('idList.xml')/ids/id[$c]"/>
</xsl:attribute>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
</xsl:transform>
我尝试将 <xsl:attribute name="num"><xsl:value-of select="$c"/></xsl:attribute>
添加到 xsl 以查看每次迭代时变量是什么,它从 1 开始,每次通过 for-each
递增,正如我所期望的所以我不知道为什么它不起作用。
任何帮助将不胜感激。
您只需稍微更改指令以检索所需索引处的元素,使用 position()
:
<xsl:value-of select="document('idList.xml')/ids/id[position() = $c]"/>
我建议你使用一个变量来避免多次解析文档:
<xsl:template match="dataSets">
<xsl:variable name="idList" select="document('idList.xml')/ids"/>
<xsl:for-each select="data">
<xsl:variable name="c">
<xsl:number value="count(preceding-sibling::*|self::*)"/>
</xsl:variable>
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="$idList/id[position() = $c]"/>
</xsl:attribute>
<xsl:apply-templates select="node()|@*"/>
<xsl:value-of select="$c" />
</xsl:copy>
</xsl:for-each>
</xsl:template>
怎么样:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:variable name="i" select="count(preceding::data) + 1" />
<xsl:attribute name="id">
<xsl:value-of select="document('idList.xml')/ids/id[$i]"/>
</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
注:
如果所有 data
元素都是同一 dataSets
父级下的兄弟姐妹,您可以通过仅计算前面的兄弟姐妹来加快此过程:
<xsl:variable name="i" select="count(preceding-sibling::data) + 1" />
可能有更好的方法将 GUID 分配给节点。
补充说明:
您的尝试失败的原因有两个:
首先,您定义变量的方式:
<xsl:variable name="c">
<xsl:number value="count(preceding-sibling::*|self::*)"/>
</xsl:variable>
导致变量成为 结果树片段。该变量包含单个根节点,xsl:number
生成的字符串是该节点的子节点。
接下来,您尝试在表达式中将变量用作数字谓词:
<xsl:value-of select="document('idList.xml')/ids/id[$c]"/>
但是,由于变量 不是 数字,它被评估为布尔值 - 并且非空它 return 为真,导致 所有 id
个节点以通过测试(当然,在 XSLT 1.0 xsl:value
中只会 return 第一个节点的值)。
如何修复:
您可以通过三种方式解决此问题:
不要将变量用作数字谓词。相反,把它放在一个表达式中,将它与位置明确地进行比较(正如@potame 在回答中所建议的那样):
<xsl:value-of select="document('idList.xml')/ids/id[position()=$c]"/>
在将其用作谓词之前将其转换为数字:
<xsl:value-of select="document('idList.xml')/ids/id[number($c)]"/>
通过使用 select
属性定义变量从一开始就消除了问题,这将导致变量变成数字开头:
<xsl:variable name="c" select="count(preceding-sibling::*|self::*)" />
另请参阅:
http://www.w3.org/TR/xslt/#variable-values
http://www.w3.org/TR/xpath/#predicates
我正在尝试使用 xslt 执行 xml 到 xml 的转换,以便为每个元素指定一个 GUID,从外部 xml 文档中按顺序提取。
来源xml:
<?xml version="1.0"?>
<dataSets>
<data name="foo"/>
<data name="bar"/>
...
</dataSets>
ID 列表:
<?xml version="1.0"?>
<ids>
<id>some-GUID</id>
<id>another-GUID</id>
...
</ids>
期望的输出:
<?xml version="1.0"?>
<dataSets>
<data name="foo" id="some-GUID"/>
<data name="bar" id="another-GUID"/>
...
</dataSets>
但我每次都得到相同的第一个 ID:
<?xml version="1.0"?>
<dataSets>
<data name="foo" id="some-GUID"/>
<data name="bar" id="some-GUID"/>
...
</dataSets>
到目前为止,这是我得到的 xsl:
<?xml version="1.0"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/|node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="dataSets">
<xsl:for-each select="data">
<xsl:variable name="c">
<xsl:number value="count(preceding-sibling::*|self::*)"/>
</xsl:variable>
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="document('idList.xml')/ids/id[$c]"/>
</xsl:attribute>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
</xsl:transform>
我尝试将 <xsl:attribute name="num"><xsl:value-of select="$c"/></xsl:attribute>
添加到 xsl 以查看每次迭代时变量是什么,它从 1 开始,每次通过 for-each
递增,正如我所期望的所以我不知道为什么它不起作用。
任何帮助将不胜感激。
您只需稍微更改指令以检索所需索引处的元素,使用 position()
:
<xsl:value-of select="document('idList.xml')/ids/id[position() = $c]"/>
我建议你使用一个变量来避免多次解析文档:
<xsl:template match="dataSets">
<xsl:variable name="idList" select="document('idList.xml')/ids"/>
<xsl:for-each select="data">
<xsl:variable name="c">
<xsl:number value="count(preceding-sibling::*|self::*)"/>
</xsl:variable>
<xsl:copy>
<xsl:attribute name="id">
<xsl:value-of select="$idList/id[position() = $c]"/>
</xsl:attribute>
<xsl:apply-templates select="node()|@*"/>
<xsl:value-of select="$c" />
</xsl:copy>
</xsl:for-each>
</xsl:template>
怎么样:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:variable name="i" select="count(preceding::data) + 1" />
<xsl:attribute name="id">
<xsl:value-of select="document('idList.xml')/ids/id[$i]"/>
</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
注:
如果所有
data
元素都是同一dataSets
父级下的兄弟姐妹,您可以通过仅计算前面的兄弟姐妹来加快此过程:<xsl:variable name="i" select="count(preceding-sibling::data) + 1" />
可能有更好的方法将 GUID 分配给节点。
补充说明:
您的尝试失败的原因有两个:
首先,您定义变量的方式:
<xsl:variable name="c">
<xsl:number value="count(preceding-sibling::*|self::*)"/>
</xsl:variable>
导致变量成为 结果树片段。该变量包含单个根节点,xsl:number
生成的字符串是该节点的子节点。
接下来,您尝试在表达式中将变量用作数字谓词:
<xsl:value-of select="document('idList.xml')/ids/id[$c]"/>
但是,由于变量 不是 数字,它被评估为布尔值 - 并且非空它 return 为真,导致 所有 id
个节点以通过测试(当然,在 XSLT 1.0 xsl:value
中只会 return 第一个节点的值)。
如何修复:
您可以通过三种方式解决此问题:
不要将变量用作数字谓词。相反,把它放在一个表达式中,将它与位置明确地进行比较(正如@potame 在回答中所建议的那样):
<xsl:value-of select="document('idList.xml')/ids/id[position()=$c]"/>
在将其用作谓词之前将其转换为数字:
<xsl:value-of select="document('idList.xml')/ids/id[number($c)]"/>
通过使用
select
属性定义变量从一开始就消除了问题,这将导致变量变成数字开头:<xsl:variable name="c" select="count(preceding-sibling::*|self::*)" />
另请参阅:
http://www.w3.org/TR/xslt/#variable-values
http://www.w3.org/TR/xpath/#predicates