(XSLT、コード最適化) sibling-nodes ..の値を参照してノードを出力するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/2287740

  •  21-09-2019
  •  | 
  •  

質問

XSLTを使用してXMLをXMLに変換しています。目的はタグの値を読み取ることです。 <node1>, 、null の場合は、次の値を割り当てる必要があります。 <node2>, 、場合によっては <node2>, も null の場合、デフォルトのテキスト「Default」を割り当てる必要があります。両方のタグに..
編集: もし <node2>null であり、 <node1> そうではありません..コードは更新されないはずです <node2>'Default' テキストですが、そのまま変換する必要があります。

これは私が試しているテスト XML です。

<root>
    <node1></node1>
    <node2></node2>
  <parent>
    <node1>data1</node1>
    <node2></node2>
  </parent>
  <parent>
    <node1></node1>
    <node2>data2</node2>
  </parent>
  <parent>
    <node1>data1</node1>
    <node2>data2</node2>
  </parent>
</root>

これが私が設計した XSLT コードです。

   <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
  <xsl:template name="template1" match="node2[(following-sibling::node1[.='']|preceding-sibling::node1[.=''])]">
    <xsl:choose>
      <xsl:when test=".=''">
        <node1><xsl:text>Default</xsl:text></node1>
        <node2><xsl:text>Default</xsl:text></node2>
      </xsl:when>
      <xsl:otherwise>
        <node1>
          <xsl:value-of select="text()"/>
        </node1>
        <xsl:copy>
          <xsl:apply-templates select="node()"/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="template2" match="node1[.='']"/>

私のコードは機能しますが、コードがかさばる点に満足していません。とにかく冗長な行(ある場合)を削除する方法はありますか...また、これを達成するために 2 つのテンプレート (つまり template1 と template2) を使用する代わりに、テンプレートの数を減らすことはできますか?

役に立ちましたか?

解決

私。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:variable name="vReplacement">Default</xsl:variable>

       <xsl:variable name="vRep" select=
        "document('')/*/xsl:variable[@name='vReplacement']/text()"/>

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

     <xsl:template match="node1[not(node())] | node2[../node1[not(node())]]">
      <xsl:copy>
          <xsl:copy-of select="../node2/text() | $vRep[not(current()/../node2/text())]"/>
      </xsl:copy>
     </xsl:template>
</xsl:stylesheet>

短くてシンプルです 現在のソリューションより -- 7 行減り、 さらに重要なのは、テンプレートが 1 つ減るということです 現在選択されているソリューションよりも。

さらに重要なことは, 、このソリューションは完全に宣言的でプッシュ スタイルです。名前付きテンプレートの呼び出しはなく、唯一の <xsl:apply-templates> はアイデンティティルール内にあります。

II.XSLT 2.0 ソリューション

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

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

 <xsl:template match="node1[not(node())] | node2[../node1[not(node())]]">
  <xsl:copy>
      <xsl:sequence select="(../node2/text(), 'Default')[1]"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

XPath 2.0 シーケンスの機能を利用するこのソリューションは、XSLT 1.0 ソリューションよりもかなり短くなります。.

XSLT 1.0 では同様のことは不可能です (述語を指定せずに 2 つのノードの結合の最初のノードを選択して 2 つのノードを相互排他にするなど)、デフォルトのテキストを持つノードとノード 1/ノード 2 ノードは異なるドキュメントに属しており、よく知られているように、ノードの順序が異なるためです。異なるドキュメントのノード間の距離は実装固有であり、保証/規定されていません。

このソリューションは完全に宣言的です (if/then/else はありません) 完全にプッシュ スタイル:唯一の <xsl:apply-templates> はアイデンティティルール内にあります。

他のヒント

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="node1[.=''] | node2[.='']">
    <xsl:copy>
      <xsl:call-template name="GetOwnValue" />
    </xsl:copy>
  </xsl:template>

  <xsl:template name="GetOwnValue">
    <xsl:variable name="node2" select="following-sibling::node2[1]" />
    <xsl:choose>
      <xsl:when test="$node2 != ''">
        <xsl:value-of select="$node2" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>Default</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

修正しました トマラックの答えで要件を達成しました。
私の質問で述べたように、このコードは、兄弟ノード1がnullでない場合(また兄弟ノード1が存在しない場合)、ノード2を(nullの場合)nullとして渡します。

このコードは最終的に、Q に投稿したコードの代替となりました。(完璧とは言えませんが…でも試してみることができてよかったです。:-)
そして、このコードは私のコードよりも 10 ~ 20 ミリ秒ほど効率的です。:-)

さあ、行きます..

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

  <xsl:template match="node1[.=''] | node2[.='']">
    <xsl:copy>
      <xsl:call-template name="GetOwnValue">
        <xsl:with-param name="node">
          <xsl:value-of select="name()"/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:copy>
  </xsl:template>

  <xsl:template name="GetOwnValue">
    <xsl:param name="node"/>
    <xsl:variable name="node2" select="following-sibling::node2[1]|preceding-sibling::node2[1]" />
    <xsl:variable name="node1" select="following-sibling::node1[1]|preceding-sibling::node1[1]" />
     <xsl:choose>
      <xsl:when test="$node2 != ''">
          <xsl:value-of select="$node2" />
      </xsl:when>
       <xsl:when test="$node!='node1' and ($node1!='' or not(following-sibling::node1[1]|preceding-sibling::node1[1]))"/>
      <xsl:otherwise>
          <xsl:text>Default</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

XSLT 2.0を使用すると、私はこれを行うだろうが、あなたはとにかく読みやすくなります。

<xsl:template match="node1[.='']">
    <xsl:copy>
        <xsl:value-of select="if (following-sibling::node2[.!='']) then following-sibling::node2[.!=''] else if (preceding-sibling::node2[.!='']) then preceding-sibling::node2[.!=''] else 'Default'"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="node2[.='']">
    <xsl:copy>
        <xsl:value-of select="if (following-sibling::node1[.!='']) then '' else if (preceding-sibling::node1[.!='']) then '' else 'Default'"/>
    </xsl:copy>
</xsl:template>
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top