sorting in xslt using attrribute from a different

2019-09-13 23:26发布

问题:

I am trying to sort a list, based on an attribute that is looked up from a different node.

<Invoice>
  <Order ID="1" OrderNumber="C" />
  <Order ID="2" OrderNumber="B" />
  <Order ID="3" OrderNumber="A" />
  <InvoiceItem ID="1" OrderID="2">Item 1</InvoiceItem>
  <InvoiceItem ID="2" OrderID="3">Item 2</InvoiceItem>
  <InvoiceItem ID="3" OrderID="2">Item 3</InvoiceItem>
  <InvoiceItem ID="4" OrderID="1">Item 4</InvoiceItem>
</Invoice>

I want to sort the InvoiceItem nodes based on OrderNumber, like this:

<Invoice>
  <InvoiceItem ID="2" OrderID="3">Item 2</InvoiceItem> <!--OrderNumber="A"-->
  <InvoiceItem ID="1" OrderID="2">Item 1</InvoiceItem> <!--OrderNumber="B"-->
  <InvoiceItem ID="3" OrderID="2">Item 3</InvoiceItem> <!--OrderNumber="B"-->
  <InvoiceItem ID="4" OrderID="1">Item 4</InvoiceItem> <!--OrderNumber="C"-->
</Invoice>

I thought I needed to use like to do something like

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@*"  />
      <xsl:apply-templates select="InvoiceItem">
        <xsl:sort select="../Order[@ID={@OrderID}]/@OrderNumber"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="InvoiceItem">
    <InvoiceItem>
      <xsl:apply-templates select="@* | node()" />
    </InvoiceItem>
  </xsl:template>

</xsl:stylesheet>

but it doesn't like the {} in the sort. If I omit them it looks for an OrderID attribute within the Order node, rather than within InvoiceItem. So in short, I need a way of looking up an attribute from a different node within a sort.

Hope this makes sense!

Is what I'm trying to do possible? If I denormalised the XML to include an OrderNumber attribute within each InvoiceItem then it would be trivial, but unfortunately the input format isn't within my control...

回答1:

Define a key

<xsl:key name="order" match="Invoice/Order" use="ID"/>

then write

<xsl:template match="Invoice">
  <xsl:copy>
    <xsl:apply-templates select="InvoiceItem">
      <xsl:sort select="key('order', @OrderID)/@OrderNumber" data-type="text"/>
    </xsl:apply-templates>
  </xsl:copy>
</xsl:template>


回答2:

Martin's xsl:key solution is more efficient, but you were close with your original attempt. You just needed to add current() and remove the {}.

<xsl:sort select="../Order[@ID=current()/@OrderID]/@OrderNumber"/>


标签: xslt