1

XML:

<root>
  <item>
    <key>mustBeSECONDKey</key>
    <value>MustBeSECONDValue</value>
  </item>
  <item>
    <key>mustBeFIRSTKey</key>
    <value>MustBeFIRSTValue</value>
  </item>
</root>

XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <file>
      <xsl:for-each select="root/item">
        <xsl:if test="key[text()='mustBeFIRSTKey']">
          <xsl:element name="someCustomTagName">
            <xsl:value-of select="value" />
          </xsl:element>
        </xsl:if>
      </xsl:for-each>
      <xsl:for-each select="root/item">
        <xsl:if test="key[text()='mustBeSECONDKey']">
          <xsl:element name="anotherNameOfATag">
            <xsl:value-of select="value" />
          </xsl:element>
        </xsl:if>
      </xsl:for-each>
    </file>
  </xsl:template>
</xsl:stylesheet>

出力:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<file>
  <someCustomTagName>MustBeFIRSTValue</someCustomTagName>
  <anotherNameOfATag>MustBeSECONDValue</anotherNameOfATag>
</file>

ここでの考え方は、指定した順序でタグが出力ドキュメントに収まるようにすることです。だから私は基本的に「すべてを調べて最初のものを見つけ、次にすべてを調べて2番目のものを見つける」と言うことでそれをやっています。これは明らかに機能します。

ただし、これがこの質問の核心になりますが、この目標を達成するためのより効率的な方法 (おそらく複数の方法) が必要だと思います。それは何ですか?

そしてさらにしわがあります。にとmustBeFIRSTKeyの 2 つの可能な値があるMustBeFIRSTValueUnoとしMustBeFIRSTValueNiます。次に、これら 2 つの値を別の 2 つの値のセットGazpachoおよびSushiにそれぞれマップします。そう

<item>
  <key>mustBeFIRSTKey</key>
  <value>MustBeFIRSTValueNi</value>
</item>

になる

<mustBeFIRSTKey>Sushi</mustBeFIRSTKey>

編集:私の問題は、ほとんど Java 側を見た概念的な問題であることがわかりました。私のセットアップ中にTransformer私はこれをやっていました:

StreamSource xsltSource = new StreamSource(ClassLoader.getSystemResourceAsStream(transformLocation));

私はこれをしていたはずです:

StreamSource xsltSource = new StreamSource(ClassLoader.getSystemResource(transformLocation).toString());

そのStreamSourceコンストラクターは を設定しますsystemId。これにより、次のように @Ian_Robert のより賢いソリューションを以下から使用できます。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!-- I discovered I could merge the $map and $inlineMap variables. -->
  <xsl:variable name="inlineMap" select="document('')//xsl:variable[@name = 'inlineMap']/map">
    <map>
      <key from="mustBeFIRSTKey" to="someCustomTagName" />
      <key from="mustBeSECONDKey" to="anotherNameOfATag">
        <value from="MustBeSECONDValueUno" to="Gazpacho" />
        <value from="MustBeSECONDValueNi" to="Sushi" />
      </key>
    </map>
  </xsl:variable>
  <xsl:key name="valueMap" match="value" use="concat(../@from, '|', @from)" />
  <xsl:template match="root">
    <xsl:variable name="items" select="item" />
    <file>
      <xsl:for-each select="$inlineMap">
        <xsl:for-each select="key">
          <xsl:apply-templates select="$items[key = current()/@from]">
            <xsl:with-param name="elementName" select="@to"/>
          </xsl:apply-templates>
        </xsl:for-each>
      </xsl:for-each>
    </file>
  </xsl:template>
  <xsl:template match="item">
    <xsl:param name="elementName" />
    <xsl:variable name="currentItem" select="." />
    <xsl:element name="{$elementName}">
      <xsl:for-each select="$inlineMap">
        <xsl:variable name="value" select="key('valueMap', concat($currentItem/key, '|', $currentItem/value))" />
        <xsl:choose>
          <xsl:when test="$value">
            <xsl:value-of select="$value/@to" />
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="$currentItem/value" />
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

この例では、マッピングなしで値を処理するためのロジックを取り除いています。

4

3 に答える 3

2

<xsl:sort>これを達成するために使用できるトリックがあります

<xsl:variable name="sortOrder" select="'|MustBeFIRSTKey|MustBeSECONDKey|--' />

<xsl:template match="/">
  <file>
    <xsl:apply-templates select="root/item">
      <xsl:sort select="string-length(substring-after($sortOrder,
                                                      concat('|', key, '|')))"
                data-type="number"
                order="descending" />
    </xsl:apply-templates>
  </file>
</xsl:template>

<xsl:template match="item">
  <xsl:element name="{key}">
    <xsl:value-of select="value" />
  </xsl:element>
</xsl:template>

これにより、MustBeFIRSTKey が最初に配置され、MustBeSECONDKey が 2 番目に配置され、その後に他のキー値が元の順序で配置されます。変数内の位置に基づいて各キーの数値ソート値を生成することによって機能しsortOrderます。MustBeFIRSTKey の場合、値はstring-length('MustBeSECONDKey|--')(18) になり、MustBeSECONDKey の場合はstring-length('--')(2) になり、それ以外の場合はstring-length('')(0) になります。

タグ名を変更するには、このアプローチを拡張して、変数を使用してマッピングもエンコードできます。

<xsl:variable name="sortOrder" select="concat(
          '|MustBeFIRSTKey+someCustomTagName',
          '|MustBeSECONDKey+anotherNameOfATag',
          '|--' />

<xsl:template match="/">
  <file>
    <xsl:apply-templates select="root/item">
      <xsl:sort select="string-length(substring-after($sortOrder,
                                                      concat('|', key, '+')))"
                data-type="number"
                order="descending" />
    </xsl:apply-templates>
  </file>
</xsl:template>

<!-- for keys that have a mapping -->
<xsl:template match="item[substring-after($sortOrder, concat('|', key, '+'))]">
  <xsl:element name="{substring-before(
       substring-after($sortOrder, concat('|', key, '+')), '|')}">
    <xsl:value-of select="value" />
  </xsl:element>
</xsl:template>

<!-- for keys that don't -->
<xsl:template match="item">
  <xsl:element name="{key}">
    <xsl:value-of select="value" />
  </xsl:element>
</xsl:template>

編集: 3 番目の質問については、値とキーを再マップする場合は、別のマッピング ファイルがおそらく最も明確なアプローチになると思います。

マッピング.xml

<map>
  <key from="MustBeFIRSTKey" to="someCustomTagName">
    <value from="MustBeFIRSTValueUno" to="Gazpacho" />
    <value from="MustBeFIRSTValueNi" to="Sushi" />
  </key>
  <key from="MustBeSECONDKey" to="anotherNameOfATag" />
</map>

スタイルシート

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:variable name="map" select="document('mapping.xml')/map" />

  <!-- define some keys used for looking up entries in the mapping -->
  <xsl:key name="keyMap" match="key" use="@from" />
  <xsl:key name="valMap" match="value" use="concat(../@from, '|', @from)" />

  <xsl:template match="/">
    <xsl:variable name="items" select="root/item" />
    <file>
      <!-- the key function looks up nodes in the "current document", so
           we need this for-each to switch the context to the mapping file
           for the key lookups -->
      <xsl:for-each select="$map">
        <!-- go through the keys in order -->
        <xsl:for-each select="key">
          <!-- process all items with that key... -->
          <xsl:apply-templates select="$items[key = current()/@from]">
            <!-- ... using the mapped tag name -->
            <xsl:with-param name="tagName" select="@to"/>
          </xsl:apply-templates>
        </xsl:for-each>
        <!-- now process any remaining keys that don't have a mapping -->
        <xsl:apply-templates select="$items[not(key('keyMap', key))]" />
      </xsl:for-each>
    </file>
  </xsl:template>

  <xsl:template match="item">
    <!-- tag name defaults to the key text if another name is not passed in -->
    <xsl:param name="tagName" select="key" />
    <xsl:variable name="curItem" select="." />
    <xsl:element name="{$tagName}">
      <!-- again, switch focus to the mapping file for key lookups -->
      <xsl:for-each select="$map">
        <!-- do we have a remapping for the item's *value*? -->
        <xsl:variable name="value"
            select="key('valMap', concat($curItem/key, '|', $curItem/value))" />
        <xsl:choose>
          <xsl:when test="$value"><xsl:value-of select="$value/@to" /></xsl:when>
          <xsl:otherwise><xsl:value-of select="$curItem/value" /></xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

マッピング ファイルが十分に小さい場合は、置き換えてスタイルシートにインラインで含めることができます。

  <xsl:variable name="map" select="document('mapping.xml')/map" />

  <xsl:variable name="inlineMap">
    <map>
      <key from="..." to="..."/>
      <!-- ... -->
    </map>
  </xsl:variable>

  <xsl:variable name="map"
                select="document('')//xsl:variable[@name='inlineMap']/map" />

document('')これは、スタイルシート自体の XML ツリーにアクセスできるトリックを使用しています。

最後に、 はキーがマッピング ファイルにエントリを持たないkeyMapアイテムを検索するためにのみ使用されることに注意してください。可能なすべてのキーのマッピングがある場合は、このキーを省略できます (およびこれを使用する)。<xsl:apply-templates select="$items[not(key('keyMap', key))]" />

于 2013-09-13T18:04:21.787 に答える
1

テンプレートを書くことから始めます

<xsl:template match="item">
          <xsl:element name="{key}">
            <xsl:value-of select="value" />
          </xsl:element>
</xsl:template>

今、あなたは使用することができます

  <xsl:template match="/root">
    <file>
      <xsl:apply-templates select="item[key ='MustBeFIRSTKey']"/>
      <xsl:apply-templates select="item[key = 'MustBeSECONDKey']"/>
    </file>
  </xsl:template>

XSLT 2.0 では、必要なのは

  <xsl:template match="/root">
    <file>
      <xsl:apply-templates select="item[key ='MustBeFIRSTKey'], item[key = 'MustBeSECONDKey']"/>
    </file>
  </xsl:template>

もちろん、XSLT 1.0 と 2.0 の両方でキーを定義できます。

<xsl:key name="k1" match="item" use="key"/>

次に、コードを次のように短縮します

  <xsl:template match="/root">
    <file>
      <xsl:apply-templates select="key('k1', 'MustBeFIRSTKey')"/>
      <xsl:apply-templates select="key('k1', 'MustBeSECONDKey')"/>
    </file>
  </xsl:template>

それぞれ

  <xsl:template match="/root">
    <file>
      <xsl:apply-templates select="key('k1', 'MustBeFIRSTKey'), key('k1', 'MustBeSECONDKey')"/>
    </file>
  </xsl:template>

もちろん、意図した並べ替えに応じxsl:sortて、順序を綴る代わりに使用できます。

于 2013-09-13T17:48:35.980 に答える