【问题标题】:XSLT: How do I use a concatenated string as a variable name with xsl:for-each select?XSLT:如何通过 xsl:for-each 选择使用连接字符串作为变量名?
【发布时间】:2019-07-30 23:57:06
【问题描述】:

我正在做一个系统迁移项目,我的任务是接收来自系统 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 选择块中使用新名称手动重新创建了每个参数,并且它起作用了。情况是,现在要求这些值不应该逐个硬编码,而是循环检查(以防每个用户的设备数量增加)。

所以我想声明一个 $LoopIndex 变量,并将其连接到与参数名称匹配的字符串,如下所示:

所以我在我的 for-each 部分中实现了以下 Tim 的答案,如下所示:

<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 的回答的启发,发现了一种使用 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>

【问题讨论】:

  • 您的预期输出似乎与您的输入相同?任何地方都没有ins:Parameter 的迹象...谢谢!
  • 嗨蒂姆,输出应该看起来非常相似。我的任务是基本上重命名 StringParameters。我也在下面回复了你。我从上面遗漏了一些代码,它们使用ins:Parameter 基本上重构了整个请求,但具有所需的名称。

标签: xslt foreach concatenation xslt-1.0


【解决方案1】:

我会在属性上使用模板匹配以及身份模板,而不是 xsl:for-eachxsl: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 以匹配您的实际。

【讨论】:

  • 嘿蒂姆,{.}(例如代码的第 9 行)是什么意思?它只是将传入请求中的值分配给新构造的参数吗?还有,DEVICE_ID_{substring(@name, 7)}中的数字有什么作用?难道只是告诉逻辑要递增的数字在原始参数的第7位(例如Device_)?
  • {.} 获取正在创建的属性中当前节点的值。 substring 的使用是从当前的name 属性中获取现有号码。请注意,我已经节点修改了我的答案,使其与您的预期输出相匹配。
  • 我会采用这种方法。或者每个属性替换一个模板。
  • 嘿蒂姆,我最终没有使用你的模板代码,但是在 for-each 中使用“starts-with(@name, 'Device')” 效果很好!太感谢了!现在,要弄清楚如何重新排列输出 SOAP 中的参数顺序以遵循输入 SOAP 中的顺序...
猜你喜欢
  • 2022-08-17
  • 1970-01-01
  • 2010-12-10
  • 1970-01-01
  • 1970-01-01
  • 2019-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多