XSLT 根据值对出现进行分组

XSLT Group occurrences based on a value

我通常擅长 XSLT 编程,但这让我很困惑。我认为这与 Muenchian Grouping 有关,但我不确定,因为当我尝试时结果不是我想要的,所以我可能在这里错了。我需要将以下 XML 转换为输出 XML。我只能使用 XSLT 1.0。

输入XML

        <ns2:ListOfAssetMgmt-AssetXaQe xmlns:ns2="http://system.com/CustomUI">
            <ns3:AssetMgmt-AssetXaQe xmlns:ns3="http://www.system.com/xml/QE%20%%20%Points">
                <ns3:Id>1</ns3:Id>
                <ns3:Name2>Direction</ns3:Name2>
                <ns3:TextValue>Injection</ns3:TextValue>
            </ns3:AssetMgmt-AssetXaQe>
            <ns3:AssetMgmt-AssetXaQe xmlns:ns3="http://www.system.com/xml/QE%20%%20%Points">
                <ns3:Id>1</ns3:Id>
                <ns3:Name2>MeterType</ns3:Name2>
                <ns3:TextValue>YMR</ns3:TextValue>
            </ns3:AssetMgmt-AssetXaQe>
            <ns3:AssetMgmt-AssetXaQe xmlns:ns3="http://www.system.com/xml/QE%20%%20%Points">
                <ns3:Id>2</ns3:Id>
                <ns3:Name2>LoadProfile</ns3:Name2>
                <ns3:TextValue>Wind</ns3:TextValue>
            </ns3:AssetMgmt-AssetXaQe>
            <ns3:AssetMgmt-AssetXaQe xmlns:ns3="http://www.system.com/xml/QE%20%%20%Points">
                <ns3:Id>2</ns3:Id>
                <ns3:Name2>Direction</ns3:Name2>
                <ns3:TextValue>Rotation</ns3:TextValue>
            </ns3:AssetMgmt-AssetXaQe>
            <ns3:AssetMgmt-AssetXaQe xmlns:ns3="http://www.system.com/xml/QE%20%%20%Points">
                <ns3:Id>2</ns3:Id>
                <ns3:Name2>MeterType</ns3:Name2>
                <ns3:TextValue>ZMR</ns3:TextValue>
            </ns3:AssetMgmt-AssetXaQe>
       </ns3:ListOfAssetMgmt-AssetXaQe>

输出XML

  <Assets>
    <QuotingEngineServicePointAssets>
      <MeterConfigurationId>1</MeterConfigurationId>
      <Direction>Injection</Direction>
      <MeterType>YMR</MeterType>
      <TimeOfUse></TimeOfUse>
      <Segment></Segment>
      <Tension></Tension>
      <ReadingFrequency></ReadingFrequency>
      <BillingFrequency></BillingFrequency>
      <LoadProfile></LoadProfile>
      <LocalProduction></LocalProduction>
      <DistributionTariff></DistributionTariff>
    </QuotingEngineServicePointAssets>
    <QuotingEngineServicePointAssets>
      <MeterConfigurationId>2</MeterConfigurationId>
      <Direction>Rotation</Direction>
      <MeterType>ZMR</MeterType>
      <TimeOfUse></TimeOfUse>
      <Segment></Segment>
      <Tension></Tension>
      <ReadingFrequency></ReadingFrequency>
      <BillingFrequency></BillingFrequency>
      <LoadProfile>Wind</LoadProfile>
      <LocalProduction></LocalProduction>
      <DistributionTariff></DistributionTariff>
    </QuotingEngineServicePointAssets>
 </Assets>

其他标签,如 TimeOfUse、Segment 等,如果它们存在于输入 XML 上,则可能也需要填充。如果它们在 Input XML 上不存在,则不应在 Output XML 上输出该元素,但我已在此处展示了可以输出的内容。非常感谢任何帮助。

我同意 michael.hor257k 的说法,即你的问题没有包含你想要实现的目标的文本描述。请始终包含它以帮助我们帮助您。

尽管如此,我查看了您的输入和输出,并认为我对示例背后的基本逻辑有所了解。

我会分 两个阶段构建流程:

  • 首先,计算一个所有相关 ID 的唯一列表,为以后的工作提供一些基础。你可以例如只需遍历列表并将其存储在变量中。我使用了两个循环来保持上下文干净:
<!-- get distinct IDs, variable contains <id>1</id><id>2</id> -->
<xsl:variable name="distinctValues">
    <xsl:for-each select="//AssetMgmt-AssetXaQe">
        <xsl:variable name="ctx1" select="." />
        <!-- filter out IDs that already occured to list them only once -->
        <xsl:if test="not(preceding-sibling::AssetMgmt-AssetXaQe[Id/text() = $ctx1/Id/text()])">
            <id><xsl:value-of select="$ctx1/Id/text()" /></id>
        </xsl:if>
    </xsl:for-each>
</xsl:variable>
  • 正如@michael.hor257k 指出的那样,下面的方法会更有效,但这是我不太了解的解决方案:
<xsl:key name="AssetMgmt-by-Id" match="AssetMgmt-AssetXaQe" use="Id" />

<xsl:for-each select="AssetMgmt-AssetXaQe[count(. | key('AssetMgmt-by-Id', Id)[1]) = 1]">
    <!-- do whatever needs to be done per unique ID -->
</for-each>
  • 其次,您可以遍历此列表,遍历具有相应 ID 的所有项目,并动态生成基于每个 (using xslt:element) 命名的标签 Name2-标签内容:
<xsl:variable name="$ctx2" select="." />

<xsl:for-each select="$distinctValues">
    <QuotingEngineServicePointAssets>
        <MeterConfigurationId>
            <xsl:value-of select="." />
        </MeterConfigurationId>

        <!-- iterate over all the entries that belong to this ID -->
        <xsl:for-each select="$ctx2//AssetMgmt-AssetXaQe[Id/text() = .]">
            <!-- dynamically generate tags based on the content of the Name2-Tag -->
            <xsl:element name="{./Name2}">
                <xsl:value-of select="./TextValue">
            </xsl:element>
        </xsl:for-each>
    </QuotingEngineServicePointAssets>
</xsl:for-each>

这应该可以解决问题。我把命名空间的麻烦留给你了,祝你好运!