1. ホーム
  2. xml

[解決済み] xsltで文字列を文字列に置き換える

2022-03-11 12:18:48

質問

ある文字列を別の文字列に置き換えたいのですが、それを行う例を見つけたのですが、うまくいかないようです。

<Addy>
  <Row>
  <LD>Dwelling, 1</D>
  <LN> East</LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>

  <Row>
  <LD>Logde</LD>
  <LN>North </LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>
</Addy>

次のような文字列をこのように置き換えたいのですが。

 Dwelling = FLAT
 Lodge    =  SHOP

以下は、私が使用したコードです。これは、LD要素内のすべての値を削除しただけです。

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

<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>

<lookup:data>
    <LD code="Dwelling">FLAT</LD>
     <LD code="Lodge">SHOP</LD>

</lookup:data>

<xsl:variable name="lookup" select="document('')/*/lookup:data"/>

<xsl:template match="LD/text()">
    <xsl:value-of select="$lookup/LD[@code = current()]" />
</xsl:template>

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

上の入力データに適用すると、次のようになる。

   <Addy>
  <Row>
  <LD></LD>
  <LN> East</LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>

  <Row>
  <LD></LD>
  <LN>North </LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>
  </Addy>

適切なコードで期待される結果

   <Addy>
  <Row>
  <LD>FLAT,1</D>
  <LN> East</LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>

  <Row>
  <LD>SHOP</LD>
  <LN>North </LN>
  <L>1</L>
  <Tf>Abesinia Passage</Tf>
  </Row>
  </Addy>  

解決方法は?

以下は、文字列に複数の置換を行うためのXSLT変換で、拡張関数は必要ありません。 :

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

 <my:reps>
  <rep>
   <old>Dwelling</old>
   <new>FLAT</new>
  </rep>
  <rep>
   <old>Lodge</old>
   <new>SHOP</new>
  </rep>
 </my:reps>

 <xsl:variable name="vReps" select="document('')/*/my:reps/*"/>

 <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="LD/text()" name="replace">
  <xsl:param name="pText" select="."/>

  <xsl:choose>
   <xsl:when test="not($vReps/old[contains($pText, .)])">
    <xsl:value-of select="$pText"/>
   </xsl:when>
   <xsl:otherwise>
       <xsl:call-template name="multiReplace">
        <xsl:with-param name="pText" select="$pText"/>
        <xsl:with-param name="pReps"
         select="$vReps[contains($pText, old)]"/>
       </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template name="multiReplace">
  <xsl:param name="pText"/>
  <xsl:param name="pReps"/>

  <xsl:choose>
      <xsl:when test="$pReps">
       <xsl:variable name="vRepResult">
         <xsl:call-template name="singleReplace">
           <xsl:with-param name="pText" select="$pText"/>
           <xsl:with-param name="pOld" select="$pReps[1]/old"/>
           <xsl:with-param name="pNew" select="$pReps[1]/new"/>
         </xsl:call-template>
       </xsl:variable>

       <xsl:call-template name="multiReplace">
        <xsl:with-param name="pText" select="$vRepResult"/>
        <xsl:with-param name="pReps" select="$pReps[position() >1]"/>
       </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
       <xsl:value-of select="$pText"/>
      </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template name="singleReplace">
  <xsl:param name="pText"/>
  <xsl:param name="pOld"/>
  <xsl:param name="pNew"/>

  <xsl:if test="$pText">
   <xsl:choose>
    <xsl:when test="not(contains($pText, $pOld))">
     <xsl:value-of select="$pText"/>
    </xsl:when>
    <xsl:otherwise>
     <xsl:value-of select="substring-before($pText, $pOld)"/>
     <xsl:value-of select="$pNew"/>
     <xsl:call-template name="singleReplace">
       <xsl:with-param name="pText" select="substring-after($pText, $pOld)"/>
       <xsl:with-param name="pOld" select="$pOld"/>
       <xsl:with-param name="pNew" select="$pNew"/>
     </xsl:call-template>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

提供された XML ドキュメントにこの変換が適用されたとき。

<Addy>
    <Row>
        <LD>Dwelling, 1</LD>
        <LN> East</LN>
        <L>1</L>
        <Tf>Abesinia Passage</Tf>
    </Row>
    <Row>
        <LD>Lodge</LD>
        <LN>North </LN>
        <L>1</L>
        <Tf>Abesinia Passage</Tf>
    </Row>
</Addy>

は、望んだ正しい結果を生成します。

<Addy>
   <Row>
      <LD>FLAT, 1</LD>
      <LN> East</LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
   <Row>
      <LD>SHOP</LD>
      <LN>North </LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
</Addy>

重要 :

この解答は完全で正しいものです。ショーンのものは、どちらかというと表面的なものです。

次のXML文書に適用して、2つのソリューションの結果を比較してください。 :

<Addy>
    <Row>
        <LD>Dwelling, Lodge, 1</LD>
        <LN> East</LN>
        <L>1</L>
        <Tf>Abesinia Passage</Tf>
    </Row>
    <Row>
        <LD>Lodge, Dwelling</LD>
        <LN>North </LN>
        <L>1</L>
        <Tf>Abesinia Passage</Tf>
    </Row>
</Addy>

Seanのソリューションでは、不正な置換が発生します。 :

<Addy>
   <Row>
      <LD>FLAT, Lodge, 1</LD>
      <LN> East</LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
   <Row>
      <LD>Lodge, FLAT</LD>
      <LN>North </LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
</Addy>

からの現在の正しい解答は これ の回答は、正しい置換を生成します。

<Addy>
   <Row>
      <LD>FLAT, SHOP, 1</LD>
      <LN> East</LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
   <Row>
      <LD>SHOP, FLAT</LD>
      <LN>North </LN>
      <L>1</L>
      <Tf>Abesinia Passage</Tf>
   </Row>
</Addy>

説明 :

  1. アイデンティティルール は、実行のために選択されたすべてのマッチするノードをそのままコピーします。

  2. の子テキストノードにマッチする1つのテンプレートで上書きされます。 LD 要素 -- 置換が行われなければならないノード。

  3. このテンプレートは、マッチしたテキスト・ノードがいずれかの old (文字列の値) で指定された、グローバルなインラインの my:reps 要素を使用します。便宜上、すべての my:reps/rep という名前のグローバル変数で選択されています。 $vReps という変数があり、この変数から参照されます。これらの文字列のいずれもが現在のノードに含まれていない場合、出力にコピーされる。

  4. もし、少なくとも1つの $vReps/old 要素の文字列値が、現在マッチしているテキストノードに含まれている場合、置換を行う必要があります。ここでは "multiReplace" は、現在のテキスト・ノードのすべての置換を実行する。このテンプレートにパラメータとして、現在のテキスト・ノードと、すべての $vReps/rep 要素の文字列値で、その old の子ノードが現在のテキストノードに含まれている場合、これらがすべて置換の対象となる。

  5. multiReplace という名前のテンプレートを呼び出します。 singleReplace という変数に取り込んで、最初の置換を行います。 $vRepResult . で置き換えた結果が格納されています。 $pText すべて の文字列の出現回数。 $pReps[1]/old の文字列値で $pReps[1]/new . 次に multiReplace テンプレートは自分自身を再帰的に呼び出します。 $pText パラメータ、および最初の置換を除外した置換を行うノードセット -- を、このパラメータで指定します。 $pReps パラメータで指定します。この再帰の停止条件は $pReps パラメータが空のノードセットになる。

  6. singleReplace テンプレートは、その名のとおり $pText パラメータに等しい部分文字列があれば $pOld パラメータに含まれる文字列で pNew パラメータを使用します。置換の数は1より多くてもよいが、すべて1つの置換指定==>に対してである。したがって、名前 singleReplace . 置換は再び再帰的に行われ,停止条件は $pText が空でなく、かつ $pOld .