0

次のことは単純か不可能のどちらかであるはずですが、今のところ方法がわかりませんので、お尋ねします。私の XSLT には、要素を生成するテンプレートがあり、それを再度変換する必要があります。テンプレートが、元の入力で変換の対象となる要素を出力する場合は常に、再度変換する必要があります。これにより、無限ループが可能になりますが、テンプレートを慎重に設計することで回避する必要があります。例として考えてみましょう:

入力.xml

<?xml version="1.0" encoding="utf-8" ?>
<example>
    <a />
    <b />
</example>

変換.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform 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="a">a</xsl:template>

    <xsl:template match="b">
        <B>b <a /></B>
    </xsl:template>
</xsl:transform>

現在の出力.xml

<?xml version="1.0"?>
<example>
    a
    <B>b <a/></B>
</example>

望ましい出力.xml

<?xml version="1.0"?>
<example>
    a
    <B>b a</B>
</example>

1回の変換でこれを達成するための最良の解決策は何ですか?

4

3 に答える 3

2

これは、必要な結果を生成する2 パス変換です。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">

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

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

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
    <xsl:apply-templates/>
  </xsl:variable>

  <xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)/*"/>

  <xsl:apply-templates select="$vPass1" mode="pass2"/>
 </xsl:template>

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

 <xsl:template match="replace">
  <xsl:text>&#xA;Hello world&#xA;</xsl:text>
 </xsl:template>

 <xsl:template match="@*|node()" mode="pass2">
  <xsl:call-template name="identity"/>
 </xsl:template>
</xsl:stylesheet>

提供された XML ドキュメントに適用した場合:

<example>
    <content>Lorem ipsum</content>
    <content><replace /></content>
</example>

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

<example>
   <wrapper>
Hello world
Lorem ipsum</wrapper>
   <wrapper>
Hello world

Hello world
</wrapper>
</example>

注意してください:

xsl:copy-ofXSLT 1.0 では、テンプレートを適用した結果は、悪名高いタイプの RTF (Result Tree Fragment) であり、定義により、標準の文字列関数を使用する場合を除いて、さらにアクセスして処理することはできません。

これが、ほぼすべての XSLT 1.0 プロセッサxxx:node-set()が、RTF を受け取り、XPath 式を使用して子孫にアクセスできる「通常の」ツリーに変換するベンダー固有の拡張関数を提供する理由です。ここで、xxxプレフィックスはベンダー固有の名前空間 uri にバインドする必要があります。

EXSLText:node-set()は、ほとんどの XSLT プロセッサで実装されています。したがって、EXSLT を使用すると、さまざまな XSLT プロセッサ間でかなりの程度の移植性が保証されます。

追加のマルチパス変換の例については、次を参照してください

https://stackoverflow.com/a/3200026/36305

そしてこれ

http://www.dpawson.co.uk/xsl/sect1/N169.html#d860e392

于 2012-10-06T18:25:30.147 に答える
1

すべての場合に機能するわけではありませんが、求めていることを達成するための非常に簡単な方法があります。replaceテンプレートに名前を付けて、で呼び出すだけですxsl:call-template。これには、既存のスタイルシートにいくつかの小さな変更を加えるだけで済みます。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform 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="content">
        <wrapper>
            <xsl:call-template name="replace"/>
            <xsl:apply-templates select="@*|node()" />
        </wrapper>
    </xsl:template>

    <xsl:template match="replace" name="replace">
        Hello world
    </xsl:template>
</xsl:transform>
于 2012-10-07T19:09:59.243 に答える
0

ここでの他の回答からの入力に基づいて、私の問題を解決するために思いついたものがあります。より適切な入力例として考えてみましょう:

<?xml version="1.0" encoding="utf-8" ?>
<example>
    <a />
    <b />
    <c />
</example>

要素<a />とは元の例<b /><c />同じように使用さ<replace />れますが、変換に示すように、それらのテンプレートにはそれぞれそれらへの参照がさらに含まれています。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">

    <xsl:template match="@*|node()">
        <xsl:variable name="this">
            <xsl:apply-templates select="." mode="normal" />
        </xsl:variable>
        <xsl:apply-templates select="ext:node-set($this)" mode="normal" />
    </xsl:template>

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

    <xsl:template match="a" mode="normal">a</xsl:template>

    <xsl:template match="b" mode="normal">
        <B>b <a /></B>
    </xsl:template>

    <xsl:template match="c" mode="normal">
        <C>c <b /></C>
    </xsl:template>
</xsl:transform>

この変換を入力例に適用すると、次の結果が得られます。

<?xml version="1.0"?>
<example>
    a
    <B>b a</B>
    <C>c <B>b a</B></C>
</example>

どうやらすべての再帰参照が解決されました。いくつかのより複雑な例でテストされていますが、すべての場合に機能するかどうかはわかりません.

この背後にある考え方は次のとおりです。最初の 2 つのテンプレートはコピー用です。あるものmode="normal"は単純な恒等変換であり、ないものは再帰を行います。モードは、再帰テンプレート内での無限ループを防ぐために使用されます。

これについてどう思うか、明らかな欠陥があるかどうか教えてください!

于 2012-10-07T22:12:11.317 に答える