8

次のようにフォーマットされたXMLがあります。

<products>
  <product>
    <name>Product 1</name>
    <price>
      <orig>15</orig>
      <offer>10</offer>
    </price>
  </product>
  <product>
    <name>Product 2</name>
    <price>
      <orig>13</orig>
      <offer>12</offer>
    </price>
  </product>
  <product>
    <name>Product 3</name>
    <price>
      <orig>11</orig>
    </price>
  </product>
</products>

XSLT 1.0を使用して、現在の価格に基づいて製品を(昇順または降順で)並べ替える必要があります。私の難しさは、2つの可能な価格値の低い方を並べ替える必要があるという事実と<orig>、両方が存在する<offer> かどうかにあります。

上記の例の場合、正しい順序は次のようになります。

  • 製品1(最小値= 10)
  • 製品3(最小値= 11)
  • 製品2(最小値= 12)

検索で同​​様の質問を見つけることができないようですので、どんな助けでも大歓迎です。

4

3 に答える 3

6

(XSLT 1.0 と 2.0 の両方に関する考えを含むように回答が更新されました)

I. XSLT 1.0:

min()XSLT 1.0 には;に相当するものが組み込まれていないことに注意してください。パーサーが をサポートしていると仮定するEXSLTと、その機能を利用math:min()して、以下の XSLT 2.0 バリアントと非常によく似たソリューションを実現できます。


Ⅱ.XSLT 2.0:

これは、XPath 2.0 集計関数を利用するソリューションですmin()

この XSLT 2.0 ソリューションの場合:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output omit-xml-declaration="no" indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <xsl:template match="products">
    <products>
      <xsl:apply-templates select="product">
        <xsl:sort select="min(price/offer|price/orig)"
          data-type="number" order="ascending" />
      </xsl:apply-templates> 
    </products>
  </xsl:template>

</xsl:stylesheet>

..提供された XML に適用されます。

<products>
  <product>
    <name>Product 1</name>
    <price>
      <orig>15</orig>
      <offer>10</offer>
    </price>
  </product>
  <product>
    <name>Product 2</name>
    <price>
      <orig>13</orig>
      <offer>12</offer>
    </price>
  </product>
  <product>
    <name>Product 3</name>
    <price>
      <orig>11</orig>
    </price>
  </product>
</products>

..必要な結果が生成されます。

<?xml version="1.0" encoding="UTF-8"?>
<products>
   <product>
      <name>Product 1</name>
      <price>
         <orig>15</orig>
         <offer>10</offer>
      </price>
   </product>
   <product>
      <name>Product 3</name>
      <price>
         <orig>11</orig>
      </price>
   </product>
   <product>
      <name>Product 2</name>
      <price>
         <orig>13</orig>
         <offer>12</offer>
      </price>
   </product>
</products>
于 2012-09-05T21:40:01.043 に答える
5

I. 一般的で純粋な XSLT 1.0 ソリューションがあります。これは次のように単純です。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="/*">
  <products>
   <xsl:apply-templates select="*">
    <xsl:sort data-type="number" select=
    "price/*[not(../* &lt; .)]"/>
   </xsl:apply-templates>
  </products>
 </xsl:template>
</xsl:stylesheet>

Ⅱ.andpriceに加えて他の子を持つ場合offerorig- この場合、一般解I . 上記(およびこの質問に対する他の2つの回答)は正しく機能しません。

この場合の正しい解決策は次のとおりです。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="/*">
  <products>
   <xsl:apply-templates select="*">
    <xsl:sort data-type="number" select=
    "sum(price/orig[not(../offer &lt;= .)])
   +
     sum(price/offer[not(../orig &lt; .)])
    "/>
   </xsl:apply-templates>
  </products>
 </xsl:template>
</xsl:stylesheet>

III. offerを超えないことがわかっている場合orig:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="/*">
  <products>
   <xsl:apply-templates select="*">
    <xsl:sort data-type="number" 
         select="price/offer | price/orig[not(../offer)]"/>
   </xsl:apply-templates>
  </products>
 </xsl:template>
</xsl:stylesheet>

IV. 検証:

上記の 3 つの変換すべてを、提供された XML ドキュメントに適用すると、次のようになります。

<products>
  <product>
    <name>Product 1</name>
    <price>
      <orig>15</orig>
      <offer>10</offer>
    </price>
  </product>
  <product>
    <name>Product 2</name>
    <price>
      <orig>13</orig>
      <offer>12</offer>
    </price>
  </product>
  <product>
    <name>Product 3</name>
    <price>
      <orig>11</orig>
    </price>
  </product>
</products>

必要な正しい結果を生成します

<products>
   <product>
      <name>Product 1</name>
      <price>
         <orig>15</orig>
         <offer>10</offer>
      </price>
   </product>
   <product>
      <name>Product 3</name>
      <price>
         <orig>11</orig>
      </price>
   </product>
   <product>
      <name>Product 2</name>
      <price>
         <orig>13</orig>
         <offer>12</offer>
      </price>
   </product>
</products>

解決策 II は、この XML ドキュメントに適用したときに正しい結果を生成する 3 つの唯一のものですminAcceptable( に子を追加しましたprice)。

<products>
  <product>
    <name>Product 1</name>
    <price>
      <orig>15</orig>
      <offer>10</offer>
      <minAcceptable>8</minAcceptable>
    </price>
  </product>
  <product>
    <name>Product 2</name>
    <price>
      <orig>13</orig>
      <offer>12</offer>
      <minAcceptable>6</minAcceptable>
    </price>
  </product>
  <product>
    <name>Product 3</name>
    <price>
      <orig>11</orig>
      <minAcceptable>7</minAcceptable>
    </price>
  </product>
</products>

他の回答では、この XML ドキュメントが正しく処理されないことに注意してください。

于 2012-09-06T03:19:33.973 に答える
5

EXSLT を必要としない XSLT 1.0 ソリューション:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output omit-xml-declaration="no" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="products">
        <products>
            <xsl:apply-templates select="product">
                <xsl:sort select="(price/*[not(. > ../*)])[1]"
                    data-type="number" order="ascending" />
            </xsl:apply-templates> 
        </products>
    </xsl:template>

</xsl:stylesheet>
于 2012-09-06T01:33:30.223 に答える