我如何使用 XSL 获得一组不同的 xml 节点值
how do i get a set of distinct xml node values using XSL
我有以下 XML 输入和所需的输出以及下面显示的内容。想法是在检测到 /Scripts/Script 节点和 /Scripts/Script/ 时设置 callLegSet 参数检测到 Preamble callLegSet 将包含当前 Script 元素的不同值集。我不确定这是否是考虑 xsl 的正确方法,我读到一个参数只能设置一次,所以这可能是一个问题;但如果是这样,列表如何接受新值?
谁能解释一下这是怎么回事,我该如何解决。
- XSL 文件
<xsl:stylesheet version="1.0">
<xsl:variable name="mode" select="1" />
<xsl:key name="keyCallLeg" match="text()" use="." />
<xsl:template match="Scripts/Script">
<Script>
<xsl:apply-templates>
<xsl:with-param
name="callLegSet"
select="descendant::Command[(@command='10' or @command='11') and @mode='1']/Fields/Field[@name='type']/Value/text()
[generate-id()=generate-id(key('keyCallLeg',.)[1])]">
</xsl:with-param>
</xsl:apply-templates>
</Script>
</xsl:template>
<xsl:template match="Scripts/Script/Preamble">
<xsl:param name="callLegSet"/>
<Preamble>
<pit>
<xsl:for-each select="$callLegSet">
<xsl:variable name="currElem" select="."/>
<Field type="0" name="{$currElem}" mode="{$mode}"/>
</xsl:for-each>
</pit>
</Preamble>
</xsl:template>
<xsl:template match="Commands"></xsl:template>
<xsl:template match="Command"></xsl:template>
<xsl:template match="*|@*|text()">
<xsl:copy>
<xsl:apply-templates select="*|@*|text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
- 我的输入文件
<xml>
<Scripts>
<Script>
<Preamble></Preamble>
<Commands>
<Command mode="1" command="10">
<Fields>
<Field ordinal="11" name="type">
<Value>Type_A</Value>
</Field>
</Fields>
</Command>
<Command mode="1" command="11">
<Fields>
<Field ordinal="14" name="type">
<Value>Type_A</Value>
</Field>
</Fields>
</Command>
</Commands>
</Script>
<Script>
<Preamble></Preamble>
<Commands>
<Command mode="1" command="10">
<Fields>
<Field ordinal="11" name="type">
<Value>Type_A</Value>
</Field>
</Fields>
</Command>
<Command mode="1" command="11">
<Fields>
<Field ordinal="14" name="type">
<Value>Type_B</Value>
</Field>
</Fields>
</Command>
<Command mode="1" command="10">
<Fields>
<Field ordinal="11" name="type">
<Value>Type_A</Value>
</Field>
</Fields>
</Command>
<Command mode="1" command="11">
<Fields>
<Field ordinal="14" name="type">
<Value>Type_C</Value>
</Field>
</Fields>
</Command>
</Commands>
</Script>
<Script>
<Preamble></Preamble>
<Commands>
<Command mode="1" command="10">
<Fields>
<Field ordinal="11" name="type">
<Value>Type_B</Value>
</Field>
</Fields>
</Command>
<Command mode="1" command="10">
<Fields>
<Field ordinal="11" name="type">
<Value>Type_Z</Value>
</Field>
</Fields>
</Command>
</Commands>
</Script>
</Scripts>
</xml>
- 输出
<xml>
<Scripts>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_A" mode="1" />
</pit>
</Preamble>
</Script>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_B" mode="1" />
<Field type="0" name="Type_C" mode="1" />
</pit>
</Preamble>
</Script>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_Z" mode="1" />
</pit>
</Preamble>
</Script>
</Scripts>
</xml>
- 期望的输出
<xml>
<Scripts>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_A" mode="1" />
</pit>
</Preamble>
</Script>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_A" mode="1" />
<Field type="0" name="Type_B" mode="1" />
<Field type="0" name="Type_C" mode="1" />
</pit>
</Preamble>
</Script>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_B" mode="1" />
<Field type="0" name="Type_Z" mode="1" />
</pit>
</Preamble>
</Script>
</Scripts>
</xml>
AFAICT,你想要的结果可以很简单地使用:
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:key name="k1" match="Command" use="concat(Fields/Field/Value, '|', generate-id(..))" />
<xsl:template match="/xml">
<xml>
<Scripts>
<xsl:for-each select="Scripts/Script">
<Script>
<Preamble>
<pit>
<xsl:for-each select="Commands/Command[count(. | key('k1', concat(Fields/Field/Value, '|', generate-id(..)))[1]) = 1]">
<Field type="0" name="{Fields/Field/Value}" mode="1" />
</xsl:for-each>
</pit>
</Preamble>
</Script>
</xsl:for-each>
</Scripts>
</xml>
</xsl:template>
</xsl:stylesheet>
当然,在 XSLT 2.0 或更高版本中甚至更简单,或者使用支持 EXSLT set:distinct()
扩展功能的处理器。
我有以下 XML 输入和所需的输出以及下面显示的内容。想法是在检测到 /Scripts/Script 节点和 /Scripts/Script/ 时设置 callLegSet 参数检测到 Preamble callLegSet 将包含当前 Script 元素的不同值集。我不确定这是否是考虑 xsl 的正确方法,我读到一个参数只能设置一次,所以这可能是一个问题;但如果是这样,列表如何接受新值?
谁能解释一下这是怎么回事,我该如何解决。
- XSL 文件
<xsl:stylesheet version="1.0">
<xsl:variable name="mode" select="1" />
<xsl:key name="keyCallLeg" match="text()" use="." />
<xsl:template match="Scripts/Script">
<Script>
<xsl:apply-templates>
<xsl:with-param
name="callLegSet"
select="descendant::Command[(@command='10' or @command='11') and @mode='1']/Fields/Field[@name='type']/Value/text()
[generate-id()=generate-id(key('keyCallLeg',.)[1])]">
</xsl:with-param>
</xsl:apply-templates>
</Script>
</xsl:template>
<xsl:template match="Scripts/Script/Preamble">
<xsl:param name="callLegSet"/>
<Preamble>
<pit>
<xsl:for-each select="$callLegSet">
<xsl:variable name="currElem" select="."/>
<Field type="0" name="{$currElem}" mode="{$mode}"/>
</xsl:for-each>
</pit>
</Preamble>
</xsl:template>
<xsl:template match="Commands"></xsl:template>
<xsl:template match="Command"></xsl:template>
<xsl:template match="*|@*|text()">
<xsl:copy>
<xsl:apply-templates select="*|@*|text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
- 我的输入文件
<xml>
<Scripts>
<Script>
<Preamble></Preamble>
<Commands>
<Command mode="1" command="10">
<Fields>
<Field ordinal="11" name="type">
<Value>Type_A</Value>
</Field>
</Fields>
</Command>
<Command mode="1" command="11">
<Fields>
<Field ordinal="14" name="type">
<Value>Type_A</Value>
</Field>
</Fields>
</Command>
</Commands>
</Script>
<Script>
<Preamble></Preamble>
<Commands>
<Command mode="1" command="10">
<Fields>
<Field ordinal="11" name="type">
<Value>Type_A</Value>
</Field>
</Fields>
</Command>
<Command mode="1" command="11">
<Fields>
<Field ordinal="14" name="type">
<Value>Type_B</Value>
</Field>
</Fields>
</Command>
<Command mode="1" command="10">
<Fields>
<Field ordinal="11" name="type">
<Value>Type_A</Value>
</Field>
</Fields>
</Command>
<Command mode="1" command="11">
<Fields>
<Field ordinal="14" name="type">
<Value>Type_C</Value>
</Field>
</Fields>
</Command>
</Commands>
</Script>
<Script>
<Preamble></Preamble>
<Commands>
<Command mode="1" command="10">
<Fields>
<Field ordinal="11" name="type">
<Value>Type_B</Value>
</Field>
</Fields>
</Command>
<Command mode="1" command="10">
<Fields>
<Field ordinal="11" name="type">
<Value>Type_Z</Value>
</Field>
</Fields>
</Command>
</Commands>
</Script>
</Scripts>
</xml>
- 输出
<xml>
<Scripts>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_A" mode="1" />
</pit>
</Preamble>
</Script>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_B" mode="1" />
<Field type="0" name="Type_C" mode="1" />
</pit>
</Preamble>
</Script>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_Z" mode="1" />
</pit>
</Preamble>
</Script>
</Scripts>
</xml>
- 期望的输出
<xml>
<Scripts>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_A" mode="1" />
</pit>
</Preamble>
</Script>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_A" mode="1" />
<Field type="0" name="Type_B" mode="1" />
<Field type="0" name="Type_C" mode="1" />
</pit>
</Preamble>
</Script>
<Script>
<Preamble>
<pit>
<Field type="0" name="Type_B" mode="1" />
<Field type="0" name="Type_Z" mode="1" />
</pit>
</Preamble>
</Script>
</Scripts>
</xml>
AFAICT,你想要的结果可以很简单地使用:
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:key name="k1" match="Command" use="concat(Fields/Field/Value, '|', generate-id(..))" />
<xsl:template match="/xml">
<xml>
<Scripts>
<xsl:for-each select="Scripts/Script">
<Script>
<Preamble>
<pit>
<xsl:for-each select="Commands/Command[count(. | key('k1', concat(Fields/Field/Value, '|', generate-id(..)))[1]) = 1]">
<Field type="0" name="{Fields/Field/Value}" mode="1" />
</xsl:for-each>
</pit>
</Preamble>
</Script>
</xsl:for-each>
</Scripts>
</xml>
</xsl:template>
</xsl:stylesheet>
当然,在 XSLT 2.0 或更高版本中甚至更简单,或者使用支持 EXSLT set:distinct()
扩展功能的处理器。