XSLT:如何将连接的字符串用作 xsl:for-each select 的变量名?
XSLT: How do I use a concatenated string as a variable name with xsl:for-each select?
我正在做一个系统迁移项目,我的任务是接收来自系统A的SOAP XML请求,然后将请求中的6个参数名称转换为系统B可读的新名称,并输出一个仅更改了参数的文件。 (所有参数名称已更改)
要求:
<q0:Command>
<q0:Transaction>
<q0:Operation namespace="Provisioning" name="Create" modifier="Data">
<q0:ParameterList>
<q0:StringParameter name="Action">Create</q0:StringParameter>
<q0:StructParameter name="Create">
<q0:StringParameter name="UserID">1234567X</q0:StringParameter>
<q0:StringParameter name="Device1">abcd567890</q0:StringParameter>
<q0:StringParameter name="Contract1">Postpaid</q0:StringParameter>
<q0:StringParameter name="Line1">990108024011900</q0:StringParameter>
<q0:StringParameter name="Device2">efgh567890</q0:StringParameter>
<q0:StringParameter name="Contract2">Postpaid</q0:StringParameter>
<q0:StringParameter name="Line2">990104562499105</q0:StringParameter>
</q0:StructParameter>
</q0:ParameterList>
</q0:Operation>
</q0:Transaction>
</q0:Command>
首先,我只是在 for-each select 块中使用新名称手动重新创建了每个参数,并且成功了。情况是,现在有一个要求,即这些值不应该一个一个地硬编码,而是循环检查(以防每个用户的设备数量增加)。
所以我想到声明一个 $LoopIndex 变量,并将它连接到与参数名称匹配的字符串,如下所示:
所以我在我的 for-each 部分中实施了蒂姆的回答,如下所示:
<xsl:for-each select="//q0:CommandRequestData/q0:Command/q0:Transaction/q0:Operation/q0:ParameterList/q0:StructParameter[@name='Create']">
<xsl:for-each select="q0:StringParameter">
<xsl:choose>
<xsl:when test="@name = 'UserID'">
<ins:Parameter name="USER_ID" value="{string()}" />
</xsl:when>
<xsl:when test="starts-with(@name, 'Device')">
<ins:Parameter name="concat('DEVICE_ID_', substring(., 7))" value="{string()}" />
</xsl:when>
<xsl:when test="starts-with(@name, 'Contract')">
<ins:Parameter name="concat('CONTRACT_TYPE_', substring(., 9))" value="{string()}" />
</xsl:when>
<xsl:when test="starts-with(@name, 'Line')">
<ins:Parameter name="concat('LINE_ID_', substring(., 5))" value="{string()}" />
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
而且上面的代码不起作用。当我说“@name=concat('Device', $LoopIndex)”时,页面不理解我的意思是“@name='Device1'”。 XSLT 可以实现这种事情吗?还是我需要坚持对参数分配进行硬编码?
不幸的是,这只是半成功。该页面实际上将 "concat('DEVICE_ID_', substring(., 7))" 作为参数名称的字符串,而不是执行连接。
更新:受到 Tim 的回答的启发,找到了 'simple' 使用 position() 来递增数字的方法:
<xsl:for-each select="//q0:CommandRequestData/q0:Command/q0:Transaction/q0:Operation/q0:ParameterList/q0:StructParameter[@name='Create']">
<xsl:for-each select="q0:StringParameter[@name='UserID']">
<ins:Parameter name="USER_ID" value="{string()}" />
</xsl:for-each>
<xsl:for-each select="q0:StringParameter[starts-with(@name, 'Device')]">
<xsl:variable name="DeviceIndex" select="position()" />
<ins:Parameter name="DEVICE_ID_{$DeviceIndex}" value="{string()}" />
</xsl:for-each>
<xsl:for-each select="q0:StringParameter[starts-with(@name, 'Contract')]">
<xsl:variable name="ContractIndex" select="position()" />
<ins:Parameter name="CONTRACT_TYPE_{$ContractIndex}" value="{string()}" />
</xsl:for-each>
<xsl:for-each select="q0:StringParameter[starts-with(@name, 'Line')]">
<xsl:variable name="LineIndex" select="position()" />
<ins:Parameter name="LINE_ID_{$LineIndex}" value="{string()}" />
</xsl:for-each>
</xsl:for-each>
传递给系统 B 的生成的 SOAP XML 请求现在成功如下所示。它不像 SOAP 请求那样安排......但我会接受它! :)
<q0:Command>
<q0:Transaction>
<q0:Operation namespace="Provisioning" name="Create" modifier="Data">
<q0:ParameterList>
<q0:StringParameter name="Action">Create</q0:StringParameter>
<q0:StructParameter name="Create">
<ins:Parameter name="USER_ID" value="1234567X"/>
<ins:Parameter name="DEVICE_ID_1" value="abcd567890"/>
<ins:Parameter name="DEVICE_ID_2" value="efgh567890"/>
<ins:Parameter name="CONTRACT_TYPE_1" value="Postpaid"/>
<ins:Parameter name="CONTRACT_TYPE_2" value="Postpaid"/>
<ins:Parameter name="LINE_ID_1" value="990108024011900"/>
<ins:Parameter name="LINE_ID_2" value="990104562499105"/>
</q0:StructParameter>
</q0:ParameterList>
</q0:Operation>
</q0:Transaction>
</q0:Command>
我会在属性上使用模板匹配,连同标识模板,而不是 xsl:for-each
和 xsl:choose
,只是为了保持代码更清晰(尽管解决这个问题并不是绝对必要的)你的问题)。
但要解决您的问题,您可以使用字符串函数从 @name
属性中提取当前数字。
试试这个 XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:q0="q0" >
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="q0:StructParameter[@name='Create']/q0:StringParameter/@name">
<xsl:attribute name="name">
<xsl:choose>
<xsl:when test=". = 'UserID'">
<xsl:text>USER_ID</xsl:text>
</xsl:when>
<xsl:when test="starts-with(., 'Device')">
<xsl:value-of select="concat('DEVICE_ID_', substring(., 7))" />
</xsl:when>
<xsl:when test="starts-with(., 'Contract')">
<xsl:value-of select="concat('CONTRACT_TYPE_', substring(., 9))" />
</xsl:when>
<xsl:when test="starts-with(., 'Line')">
<xsl:value-of select="concat('LINE_ID_', substring(., 5))" />
</xsl:when>
<xsl:otherwise>NA</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
请注意,您必须更改 q0
的命名空间 URI 以相应地匹配您的实际 URI。
我正在做一个系统迁移项目,我的任务是接收来自系统A的SOAP XML请求,然后将请求中的6个参数名称转换为系统B可读的新名称,并输出一个仅更改了参数的文件。 (所有参数名称已更改)
要求:
<q0:Command>
<q0:Transaction>
<q0:Operation namespace="Provisioning" name="Create" modifier="Data">
<q0:ParameterList>
<q0:StringParameter name="Action">Create</q0:StringParameter>
<q0:StructParameter name="Create">
<q0:StringParameter name="UserID">1234567X</q0:StringParameter>
<q0:StringParameter name="Device1">abcd567890</q0:StringParameter>
<q0:StringParameter name="Contract1">Postpaid</q0:StringParameter>
<q0:StringParameter name="Line1">990108024011900</q0:StringParameter>
<q0:StringParameter name="Device2">efgh567890</q0:StringParameter>
<q0:StringParameter name="Contract2">Postpaid</q0:StringParameter>
<q0:StringParameter name="Line2">990104562499105</q0:StringParameter>
</q0:StructParameter>
</q0:ParameterList>
</q0:Operation>
</q0:Transaction>
</q0:Command>
首先,我只是在 for-each select 块中使用新名称手动重新创建了每个参数,并且成功了。情况是,现在有一个要求,即这些值不应该一个一个地硬编码,而是循环检查(以防每个用户的设备数量增加)。
所以我想到声明一个 $LoopIndex 变量,并将它连接到与参数名称匹配的字符串,如下所示:
所以我在我的 for-each 部分中实施了蒂姆的回答,如下所示:
<xsl:for-each select="//q0:CommandRequestData/q0:Command/q0:Transaction/q0:Operation/q0:ParameterList/q0:StructParameter[@name='Create']">
<xsl:for-each select="q0:StringParameter">
<xsl:choose>
<xsl:when test="@name = 'UserID'">
<ins:Parameter name="USER_ID" value="{string()}" />
</xsl:when>
<xsl:when test="starts-with(@name, 'Device')">
<ins:Parameter name="concat('DEVICE_ID_', substring(., 7))" value="{string()}" />
</xsl:when>
<xsl:when test="starts-with(@name, 'Contract')">
<ins:Parameter name="concat('CONTRACT_TYPE_', substring(., 9))" value="{string()}" />
</xsl:when>
<xsl:when test="starts-with(@name, 'Line')">
<ins:Parameter name="concat('LINE_ID_', substring(., 5))" value="{string()}" />
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
而且上面的代码不起作用。当我说“@name=concat('Device', $LoopIndex)”时,页面不理解我的意思是“@name='Device1'”。 XSLT 可以实现这种事情吗?还是我需要坚持对参数分配进行硬编码?
不幸的是,这只是半成功。该页面实际上将 "concat('DEVICE_ID_', substring(., 7))" 作为参数名称的字符串,而不是执行连接。
更新:受到 Tim 的回答的启发,找到了 'simple' 使用 position() 来递增数字的方法:
<xsl:for-each select="//q0:CommandRequestData/q0:Command/q0:Transaction/q0:Operation/q0:ParameterList/q0:StructParameter[@name='Create']">
<xsl:for-each select="q0:StringParameter[@name='UserID']">
<ins:Parameter name="USER_ID" value="{string()}" />
</xsl:for-each>
<xsl:for-each select="q0:StringParameter[starts-with(@name, 'Device')]">
<xsl:variable name="DeviceIndex" select="position()" />
<ins:Parameter name="DEVICE_ID_{$DeviceIndex}" value="{string()}" />
</xsl:for-each>
<xsl:for-each select="q0:StringParameter[starts-with(@name, 'Contract')]">
<xsl:variable name="ContractIndex" select="position()" />
<ins:Parameter name="CONTRACT_TYPE_{$ContractIndex}" value="{string()}" />
</xsl:for-each>
<xsl:for-each select="q0:StringParameter[starts-with(@name, 'Line')]">
<xsl:variable name="LineIndex" select="position()" />
<ins:Parameter name="LINE_ID_{$LineIndex}" value="{string()}" />
</xsl:for-each>
</xsl:for-each>
传递给系统 B 的生成的 SOAP XML 请求现在成功如下所示。它不像 SOAP 请求那样安排......但我会接受它! :)
<q0:Command>
<q0:Transaction>
<q0:Operation namespace="Provisioning" name="Create" modifier="Data">
<q0:ParameterList>
<q0:StringParameter name="Action">Create</q0:StringParameter>
<q0:StructParameter name="Create">
<ins:Parameter name="USER_ID" value="1234567X"/>
<ins:Parameter name="DEVICE_ID_1" value="abcd567890"/>
<ins:Parameter name="DEVICE_ID_2" value="efgh567890"/>
<ins:Parameter name="CONTRACT_TYPE_1" value="Postpaid"/>
<ins:Parameter name="CONTRACT_TYPE_2" value="Postpaid"/>
<ins:Parameter name="LINE_ID_1" value="990108024011900"/>
<ins:Parameter name="LINE_ID_2" value="990104562499105"/>
</q0:StructParameter>
</q0:ParameterList>
</q0:Operation>
</q0:Transaction>
</q0:Command>
我会在属性上使用模板匹配,连同标识模板,而不是 xsl:for-each
和 xsl:choose
,只是为了保持代码更清晰(尽管解决这个问题并不是绝对必要的)你的问题)。
但要解决您的问题,您可以使用字符串函数从 @name
属性中提取当前数字。
试试这个 XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:q0="q0" >
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="q0:StructParameter[@name='Create']/q0:StringParameter/@name">
<xsl:attribute name="name">
<xsl:choose>
<xsl:when test=". = 'UserID'">
<xsl:text>USER_ID</xsl:text>
</xsl:when>
<xsl:when test="starts-with(., 'Device')">
<xsl:value-of select="concat('DEVICE_ID_', substring(., 7))" />
</xsl:when>
<xsl:when test="starts-with(., 'Contract')">
<xsl:value-of select="concat('CONTRACT_TYPE_', substring(., 9))" />
</xsl:when>
<xsl:when test="starts-with(., 'Line')">
<xsl:value-of select="concat('LINE_ID_', substring(., 5))" />
</xsl:when>
<xsl:otherwise>NA</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
请注意,您必须更改 q0
的命名空间 URI 以相应地匹配您的实际 URI。