2

私は XSLT を初めて使用します。次のような XML を入力した場合、私の無知を許してください。

<Doc>
 <stuff>
   <for var="i" from="1" to="2">
      <item>$(i)></item>
      <for var="j" from="2" to="4">
        <innerItem>$(j)</innerItem>
      </for>
    </for>    
  </stuff>
</Doc>

変換を使用して、出力 XML を次のように展開したいと考えています。

<Doc>
 <stuff>
   <item>1</item>
     <innerItem>2</innerItem>
     <innerItem>3</innerItem>
     <innerItem>4</innerItem>
   <item>2</item>
     <innerItem>2</innerItem>
     <innerItem>3</innerItem>
     <innerItem>4</innerItem>
 </stuff>
</Doc>

私が持っているのはこれだけです:次に何をしますか?

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>

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

  <xsl:template match="for">
    <xsl:variable name="from" select="@from" />
    <xsl:variable name="to" select="@to" />
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>    
  </xsl:template>
</xsl:stylesheet>
4

2 に答える 2

2

これははるかに単純で(明示的な条件付き命令、ユーザー定義関数、モードなし)、より短く、機能する変換です。

<xsl:stylesheet version="2.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:param name="pVars" as="element()*"/>
  <xsl:copy>
   <xsl:apply-templates select="node()|@*">
     <xsl:with-param name="pVars" select="$pVars"/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="for">
  <xsl:param name="pVars" as="element()*"/>

  <xsl:variable name="vCurrentFor" select="."/>

  <xsl:for-each select="@from to @to">
      <xsl:variable name="vnewVars">
        <xsl:sequence select="$pVars"/>
        <var name="{$vCurrentFor/@var}" value="{current()}"/>
      </xsl:variable>

    <xsl:apply-templates select="$vCurrentFor/node()">
      <xsl:with-param name="pVars" select="$vnewVars/*"/>
    </xsl:apply-templates>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="text()[contains(., '$(')]">
  <xsl:param name="pVars" as="element()*"/>

  <xsl:analyze-string select="."
    regex="\$\((.+?)\)">
     <xsl:non-matching-substring>
       <xsl:value-of select="."/>
     </xsl:non-matching-substring>
     <xsl:matching-substring>
       <xsl:variable name="vName" select="regex-group(1)"/>

       <xsl:variable name="vReplacement" select=
        "$pVars[@name eq $vName][last()]/@value"/>
       <xsl:sequence select="string($vReplacement)"/>
     </xsl:matching-substring>
  </xsl:analyze-string>
 </xsl:template>
</xsl:stylesheet>

この変換が提供されたXMLドキュメントに適用される場合:

<Doc>
    <stuff>
        <for var="i" from="1" to="2">
            <item>$(i)</item>
            <for var="j" from="2" to="4">
                <innerItem>$(j)</innerItem>
            </for>
        </for>
    </stuff>
</Doc>

必要な正しい結果が生成されます。

<Doc>
   <stuff>
      <item>1</item>
      <innerItem>2</innerItem>
      <innerItem>3</innerItem>
      <innerItem>4</innerItem>
      <item>2</item>
      <innerItem>2</innerItem>
      <innerItem>3</innerItem>
      <innerItem>4</innerItem>
   </stuff>
</Doc>

はるかに複雑な処理を実行することが可能です

異なるレベルの変数を一緒に使用する

<Doc>
    <stuff>
        <for var="x" from="1" to="2">
            <item>$(x)</item>
            <for var="y" from="2" to="4">
                <innerItem>$(x).$(y)</innerItem>
            </for>
        </for>
    </stuff>
</Doc>

このドキュメントの結果は次のとおりです。

<Doc>
   <stuff>
      <item>1</item>
      <innerItem>1.2</innerItem>
      <innerItem>1.3</innerItem>
      <innerItem>1.4</innerItem>
      <item>2</item>
      <innerItem>2.2</innerItem>
      <innerItem>2.3</innerItem>
      <innerItem>2.4</innerItem>
   </stuff>
</Doc>

またはこのXMLドキュメントで

<Doc>
    <stuff>
        <for var="x" from="1" to="2">
          <item>
             <value>$(x)</value>
                <for var="y" from="2" to="4">
                    <innerItem>
                      <value>$(x).$(y)</value>
                        <for var="z" from="3" to="5">
                          <inner-most-Item>$(x).$(y).$(z)</inner-most-Item>
                        </for>
                    </innerItem>
                </for>
          </item>
        </for>
    </stuff>
</Doc>

結果は

<Doc>
   <stuff>
      <item>
         <value>1</value>
         <innerItem>
            <value>1.2</value>
            <inner-most-Item>1.2.3</inner-most-Item>
            <inner-most-Item>1.2.4</inner-most-Item>
            <inner-most-Item>1.2.5</inner-most-Item>
         </innerItem>
         <innerItem>
            <value>1.3</value>
            <inner-most-Item>1.3.3</inner-most-Item>
            <inner-most-Item>1.3.4</inner-most-Item>
            <inner-most-Item>1.3.5</inner-most-Item>
         </innerItem>
         <innerItem>
            <value>1.4</value>
            <inner-most-Item>1.4.3</inner-most-Item>
            <inner-most-Item>1.4.4</inner-most-Item>
            <inner-most-Item>1.4.5</inner-most-Item>
         </innerItem>
      </item>
      <item>
         <value>2</value>
         <innerItem>
            <value>2.2</value>
            <inner-most-Item>2.2.3</inner-most-Item>
            <inner-most-Item>2.2.4</inner-most-Item>
            <inner-most-Item>2.2.5</inner-most-Item>
         </innerItem>
         <innerItem>
            <value>2.3</value>
            <inner-most-Item>2.3.3</inner-most-Item>
            <inner-most-Item>2.3.4</inner-most-Item>
            <inner-most-Item>2.3.5</inner-most-Item>
         </innerItem>
         <innerItem>
            <value>2.4</value>
            <inner-most-Item>2.4.3</inner-most-Item>
            <inner-most-Item>2.4.4</inner-most-Item>
            <inner-most-Item>2.4.5</inner-most-Item>
         </innerItem>
      </item>
   </stuff>
</Doc>

ここでやめますが、この言語の優れたデザインを考えると、可能性は無限です。

更新:OPは尋ねました:

「属性内の拡張も許可する方法はありますか?例:<inner-most-Item id="$(i)">

はい、これは非常に簡単です。属性に一致する新しいテンプレートを追加し、コードをリファクタリングするだけです

<xsl:stylesheet version="2.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="*"/>

     <xsl:template match="node()|@*">
      <xsl:param name="pVars" as="element()*"/>
      <xsl:copy>
       <xsl:apply-templates select="node()|@*">
         <xsl:with-param name="pVars" select="$pVars"/>
       </xsl:apply-templates>
      </xsl:copy>
     </xsl:template>

     <xsl:template match="for">
      <xsl:param name="pVars" as="element()*"/>

      <xsl:variable name="vCurrentFor" select="."/>

      <xsl:for-each select="@from to @to">
          <xsl:variable name="vnewVars">
            <xsl:sequence select="$pVars"/>
            <var name="{$vCurrentFor/@var}" value="{current()}"/>
          </xsl:variable>

        <xsl:apply-templates select="$vCurrentFor/node()">
          <xsl:with-param name="pVars" select="$vnewVars/*"/>
        </xsl:apply-templates>
      </xsl:for-each>
     </xsl:template>

     <xsl:template match="text()[contains(., '$(')]">
      <xsl:param name="pVars" as="element()*"/>

      <xsl:value-of select="my:evalText($pVars, .)"/>
     </xsl:template>

     <xsl:template match="@*[contains(., '$(')]">
      <xsl:param name="pVars" as="element()*"/>

      <xsl:attribute name="{name()}">
        <xsl:value-of select="my:evalText($pVars, .)"/>
      </xsl:attribute> 
     </xsl:template>

     <xsl:function name="my:evalText">
      <xsl:param name="pVars" as="element()*"/>
       <xsl:param name="pText"/>

          <xsl:analyze-string select="$pText"
            regex="\$\((.+?)\)">
             <xsl:non-matching-substring>
               <xsl:value-of select="."/>
             </xsl:non-matching-substring>
             <xsl:matching-substring>
               <xsl:variable name="vName" select="regex-group(1)"/>

               <xsl:variable name="vReplacement" select=
                "$pVars[@name eq $vName][last()]/@value"/>
               <xsl:value-of select="string($vReplacement)"/>
             </xsl:matching-substring>
          </xsl:analyze-string>
     </xsl:function>
</xsl:stylesheet>

現在、この変換は次のXMLドキュメントに適用されます。

<Doc>
    <stuff>
        <for var="i" from="1" to="2">
            <item name="X$(i)">$(i)</item>
            <for var="j" from="2" to="4">
                <innerItem name="X$(i).$(j)">$(j)</innerItem>
            </for>
        </for>
    </stuff>
</Doc>

必要な正しい結果が生成されます。

<Doc>
   <stuff>
      <item name="X1">1</item>
      <innerItem name="X1.2">2</innerItem>
      <innerItem name="X1.3">3</innerItem>
      <innerItem name="X1.4">4</innerItem>
      <item name="X2">2</item>
      <innerItem name="X2.2">2</innerItem>
      <innerItem name="X2.3">3</innerItem>
      <innerItem name="X2.4">4</innerItem>
   </stuff>
</Doc>
于 2012-06-06T02:41:03.500 に答える
0

これは、提供された入力XMLで機能します。私はモードを使わなければなりませんでした。文字列置換関数は、この質問に対する回答に基づいています:XSLT文字列置換

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fun="http://www.nothing.org">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <xsl:apply-templates mode="parse"/>
    </xsl:template>
    <xsl:template name="forImpl">
        <xsl:param name="counter" select="@from"/>
        <xsl:param name="to" select="@to"/>
        <xsl:param name="varName" select="@var"/>
        <xsl:param name="nodes"/>
        <xsl:apply-templates select="$nodes" mode="parse">
            <xsl:with-param name="varName" select="concat('$(', $varName, ')')"/>
            <xsl:with-param name="counter" select="$counter"/>
        </xsl:apply-templates>
        <xsl:if test="$counter != $to">
            <xsl:call-template name="forImpl">
                <xsl:with-param name="counter" select="$counter + 1"/>
                <xsl:with-param name="to" select="$to"/>
                <xsl:with-param name="varName" select="$varName"/>
                <xsl:with-param name="nodes" select="$nodes"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    <xsl:template match="@* | node()" mode="parse">
        <xsl:param name="varName"/>
        <xsl:param name="counter"/>
        <xsl:copy>
            <xsl:apply-templates select="@* | node()" mode="parse">
                <xsl:with-param name="varName" select="$varName"/>
                <xsl:with-param name="counter" select="$counter"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="for" mode="parse">
        <xsl:param name="varName"/>
        <xsl:param name="counter"/>
        <xsl:call-template name="forImpl">
            <xsl:with-param name="counter" select="@from"/>
            <xsl:with-param name="to" select="@to"/>
            <xsl:with-param name="varName" select="@var"/>
            <xsl:with-param name="nodes">
                <xsl:copy-of select="./*"/>
            </xsl:with-param>
        </xsl:call-template>
    </xsl:template>
    <xsl:template match="text()" mode="parse">
        <xsl:param name="varName"/>
        <xsl:param name="counter"/>
        <xsl:value-of select="if (contains(., $varName)) then fun:string-replace-all(., $varName, $counter) else ."/>
    </xsl:template>
    <xsl:function name="fun:string-replace-all">
        <xsl:param name="text"/>
        <xsl:param name="replace"/>
        <xsl:param name="by"/>
        <xsl:choose>
            <xsl:when test="contains($text, $replace)">
                <xsl:value-of select="substring-before($text,$replace)"/>
                <xsl:value-of select="$by"/>
                <xsl:value-of select="fun:string-replace-all(substring-after($text,$replace), $replace, $by)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$text"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>
</xsl:stylesheet>
于 2012-06-05T20:56:37.480 に答える