2013-01-15 12 views
6

私はXSLTが新しく、したがって、属性の存在をチェックするためのベストプラクティスを知りたいと考えています。私のXMLは次のようになります。属性が存在する場合はXSLT

'item'要素の 'lang'-attributeはオプションです。

ここでは、 "lang"属性を持っているかどうかを確認しながら、-loopを使って項目をループします。そうであれば、IDを使って文字列全体を取得したい(EN - > 'English'など)。属性が設定されていない場合は、「言語が設定されていません」と似たようなものを記述します。

私は次のコードを使用しますが、より効率的な方法ではできない場合は自分自身に質問しています。

<xsl:for-each select="//root/items/item"> 
    <xsl:variable name="cur_lang" select="@lang" /> <!-- first I store the attr lang in a variable --> 
    <xsl:choose> 
     <xsl:when test="@lang"> <!-- then i test if the attr exists --> 
      <xsl:value-of select="//root/languages/lang[@id=$cur_lang]" /> <!-- if so, parse the element value --> 
     </xsl:when> 
     <xsl:otherwise> 
      No language set <!-- else --> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:for-each> 

どのようなご提案ですか?

+2

あなたは例を追加できますあなたが入力した入力を正確な出力に与える必要がありますか? –

+0

Carlo、あなたは欲しい出力を提供するのを忘れました - 私はあなたが現在の 'lang'属性値を置き換えるために拡張言語文字列を使いたいと思っていました。提供されたXML文書に変換が適用されたときに質問を編集して、望ましい結果を提供してください。 –

答えて

7

キーを使用する方が効率的かもしれません。あなたは

<xsl:key name="langByCode" match="lang" use="@id" /> 

はその後のループであなたは、単に一般的になり、全体のことをより自然なXSLTのアプローチを話す

<xsl:when test="@lang"> <!-- then i test if the attr exists --> 
    <xsl:value-of select="key('langByCode', @lang)" /> 
</xsl:when> 

しかし、言うことができ、テンプレートの外にトップレベルの要素を使用してキーを定義します代わりにfor-eachifの使用テンプレートマッチング:これらのテンプレートで

<xsl:template match="item[@lang]"> 
    <xsl:value-of select="key('langByCode', @lang)" /> 
</xsl:template> 

<xsl:template match="item"> 
    <xsl:text>No language set</xsl:text> 
</xsl:template> 

代わりにあなたがしてを行うことができますそれぞれのアイテムに対して適切なテンプレートを自動的に選択します。最も具体的なテンプレートを使用するというルールがあるため、lang属性を持つアイテムはitem[@lang]、そうでないアイテムはitemとなります。

第三の可能性がここにトリックは数として扱わboolean(@lang)1場合であるということです/他は単一のXPath式

<xsl:value-of select=" 
    substring(
    concat('No language set', key('langByCode', @lang)), 
    1 + (15 * boolean(@lang)) 
)" /> 

にチェックすると、私は全体を置くためにSOで学習した小さなトリックですlang属性が存在し、一致しない場合は0です。 lang="EN"がある場合は、文字列"No language setEnglish"を作成し、次に16番目の文字("English")で始まる部分文字列を取ります。 がなく、 lang属性の場合は、文字列"No language set"(空のノードセットの文字列値が空の文字列であるため)を作成し、最初の文字(つまり文字列全体)から始まる部分文字列を取ります。

他の属性と同じトリックを使用できます(例: :我々は、オプションのカラー属性を持っていたし、それが存在しない場合、あなたはあなたがXSLT 3.0を使用することができるしている場合

<xsl:value-of select="substring(
    concat('No color specified', @color), 
    1 + (18 * boolean(@color)) 
)" /> 
+0

ありがとう、私はを使用しようとしましたが、私はこれを行うときにページが読み込まれません。私は何が間違っていますか? – carlo

+0

から移動して問題を解決しました – carlo

+0

の余分なオプションの属性がある場合、私は何ができますか? – carlo

0

別の方法で、map(別の役に立つリンクであることを行うことができます"No color specified"を言いたかったと仮定map)。

(整形式に固定)XML入力

<root> 
    <languages> 
     <lang id="EN">English</lang> 
     <lang id="FR">French</lang> 
     <lang id="DE">German</lang> 
    </languages> 
    <items> 
     <item lang="EN">test 1</item> 
     <item>test 2</item> 
     <item lang="FR">item 3</item> 
    </items> 
</root> 

XSLT 3.0

<xsl:stylesheet version="3.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:map="http://www.w3.org/2005/xpath-functions/map" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" extension-element-prefixes="xs map"> 
    <xsl:output indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:variable name="lang-map" as="map(xs:string, xs:string)" 
     select="map:new(
     for $lang in /*/languages/lang 
     return 
      map{$lang/@id := $lang/string()} 
     )"/> 

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

    <xsl:template match="languages"/> 

    <xsl:template match="item[@lang and map:contains($lang-map,@lang)]"> 
     <item><xsl:value-of select="$lang-map(current()/@lang)"/></item> 
    </xsl:template> 

    <xsl:template match="item"> 
     <item>No language found.</item> 
    </xsl:template> 

</xsl:stylesheet> 

出力

<root> 
    <items> 
     <item>English</item> 
     <item>No language found.</item> 
     <item>French</item> 
    </items> 
</root> 
関連する問題