2009-05-06 15 views
1

渡る変換しますspecBの<Atom>と同じことを意味するので、要素の名前を変更するだけです。XSLは、私は次のようになりますXMLを持っている名前空間

<Widgets>はspecBで<Molecule>と同じことを意味しているため、要素の名前を変更してください。

ラップ<Atom>および<Molecule>は、<Widgets>という名前の要素にあります。これは、specAのとは異なる意味です。

他のものはすべてそのままコピーされますが、新しい名前空間にコピーされます。

このためにXSLTは何ですか?

SOLUTION?:最後に、私はこれで行きました:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:old="http://widgetspecA.com/ns" 
    xmlns="http://widgetspecB.com/ns" 
    exclude-result-prefixes="old"> 

    <xsl:output method="xml"/> 

    <xsl:template match="*"> 
    <xsl:element name="{name()}"> 
     <xsl:copy-of select="@*"/> 
     <xsl:apply-templates/> 
    </xsl:element> 
    </xsl:template> 

    <xsl:template match="old:SmallWidget" mode="single"> 
    <Atom> 
     <xsl:apply-templates/> 
    </Atom> 
    </xsl:template> 

    <xsl:template match="old:Widgets" mode="single"> 
     <Molecule> 
     <xsl:apply-templates/> 
     </Molecule> 
    </xsl:template> 

    <xsl:template match="old:SmallWidget[following-sibling::old:Widgets]"> 
     <Widgets> 
     <xsl:apply-templates select="self::node()" mode="single"/> 
     <xsl:apply-templates select="following-sibling::old:Widgets" mode="single"/> 
     </Widgets> 
    </xsl:template> 

    <xsl:template match="old:Widgets[preceding-sibling::old:SmallWidget]"/> 

    <xsl:template match="old:SmallWidget[not(following-sibling::old:Widgets)]"> 
     <Widgets> 
     <xsl:apply-templates select="self::node()" mode="single"/> 
     </Widgets> 
    </xsl:template> 

    <xsl:template match="old:Widgets[not(preceding-sibling::old:SmallWidget)]"> 
     <Widgets> 
     <xsl:apply-templates select="self::node()" mode="single"/> 
     </Widgets> 
    </xsl:template> 

</xsl:stylesheet> 
+0

私は任意の要素のサポートを含むようにソリューションを変更しました。 "http://widgetspecA.com/ns"以外の名前空間にあるノードはそのままコピーされ、名前空間はそのまま残されます。 – Tomalak

+0

これは一見動いているターゲットなので、私は答えを削除しました。次回は完全で曖昧ではない問題の説明を投稿してください。 – Tomalak

+0

Wayne、あなたの最終的な解決策には良い仕事。それはまだ簡素化することができます。私の編集された答えを見てください。 –

答えて

1

良いXSLTソリューションは、単純なテンプレートルールにあなたの人間が読めるルールをマッピングします。ここでは、あなたの言葉で、ルールです:specAで

  1. <SmallWidget>はspecBで<Atom>と同じことを意味し、これだけの要素の名前を変更します。
  2. <Widgets>はspecBで<Molecule>と同じことを意味していますので、要素の名前を変更してください。
  3. という名前の要素には、<Atom><Molecule>が含まれています。これは、specAの<Widgets>とは異なる意味です。
  4. 他のものはすべてそのままコピーされますが、新しい名前空間にコピーされます。

さんはそれを行くを挙げてみましょう:それはあなたがlocal-name()機能ではなくname()機能を使用することが重要だ

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:in="http://widgetspecA.com/ns" 
    xmlns="http://widgetspecB.com/ns" 
    exclude-result-prefixes="in"> 

    <!-- 1. Rename <SmallWidget> --> 
    <xsl:template mode="rename" match="in:SmallWidget">Atom</xsl:template> 

    <!-- 2. Rename <Widgets> --> 
    <xsl:template mode="rename" match="in:Widgets">Molecule</xsl:template> 

    <!-- 3. Wrap <Atom> & <Molecule> with <Widgets> --> 
    <xsl:template match="in:SmallWidget"> 
    <!-- ASSUMPTION: in:Widgets immediately follows in:SmallWidget --> 
    <Widgets> 
     <xsl:apply-templates mode="convert" select="."/> 
     <xsl:apply-templates mode="convert" select="following-sibling::in:Widgets"/> 
    </Widgets> 
    </xsl:template> 

      <!-- Skip by this in regular processing; 
       it gets explicitly converted inside <Widgets> (see above) --> 
      <xsl:template match="in:Widgets"/> 

      <!-- Also, don't copy whitespace appearing 
       immediately before in:Widgets --> 
      <xsl:template match="text() 
           [following-sibling::node()[1][self::in:Widgets]]"/> 


    <!-- 4: Everything copied as is, but in the new namespace --> 

    <!-- Copy non-element nodes as is --> 
    <xsl:template match="@* | text() | comment() | processing-instruction()"> 
     <xsl:copy/> 
    </xsl:template> 

    <!-- By default, just convert elements to new namespace 
     (exceptions under #3 above) --> 
    <xsl:template match="*"> 
     <xsl:apply-templates mode="convert" select="."/> 
    </xsl:template> 

      <xsl:template mode="convert" match="*"> 
       <!-- Optionally rename the element --> 
       <xsl:variable name="name"> 
       <xsl:apply-templates mode="rename" select="."/> 
       </xsl:variable> 
       <xsl:element name="{$name}"> 
       <xsl:apply-templates select="@* | node()"/> 
       </xsl:element> 
      </xsl:template> 

        <!-- By default, just use the same local 
         name as in the input document --> 
        <xsl:template mode="rename" match="*"> 
         <xsl:value-of select="local-name()"/> 
        </xsl:template> 

</xsl:stylesheet> 

注こと。 name()を使用すると、属性を<xsl:element>に追加してプレフィックスが表示されても名前空間を適用しない限り、スタイルシートで明示的に宣言されていない名前空間接頭辞を使用して入力文書を開始すると、スタイルシートが破損します。ただし、local-name()を使用すると安全です。プレフィックスを含むことはありませんので、result要素はスタイルシートのデフォルト名前空間を採用します。あなたが要求したまさにあなたのサンプル入力ドキュメントの利回りに対する上記のスタイルシートを実行

<Root xmlns="http://widgetspecB.com/ns">...any...<WidgetBox>...any... 
    <Widgets><Atom> 
    ...any... 
    </Atom><Molecule> 
    ...any... 
    </Molecule></Widgets>...any... 
</WidgetBox>...any...</Root> 

は、ご質問があれば私に教えてください。 XSLTは強力ではありません!

P.S.あなたの例のように空白を複製することを本当に正確にしたいのであれば、段階的な「連鎖」処理を使用することができました。ここでは、一度に1つのノードだけにテンプレートを適用し、各テンプレートルールは、次の兄弟です。しかし、それはこのような状況のために過度のように思えました。

更新日: 投稿した新しいソリューションは非常に合理的です。しかし、それはいくらか簡略化することができます。私はあなたの新しい解決法を採用し、以下に推奨されるいくつかの変更を、私が何を変更したのか、そしてなぜそれらを変更したのかを示すコメントとともに示しました。

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:old="http://widgetspecA.com/ns" 
    xmlns="http://widgetspecB.com/ns" 
    exclude-result-prefixes="old"> 

    <!-- "xml" is the default; no real need for this 
    <xsl:output method="xml"/> 
    --> 

    <!-- This works fine if you only want to copy elements, attributes, 
     and text. Just be aware that comments and PIs will get 
     effectively stripped out, because the default template rule 
     for those is to do nothing. 
    --> 
    <xsl:template match="*"> 
    <xsl:element name="{name()}"> 
     <xsl:copy-of select="@*"/> 
     <xsl:apply-templates/> 
    </xsl:element> 
    </xsl:template> 

    <xsl:template match="old:SmallWidget" mode="single"> 
    <Atom> 
     <xsl:apply-templates/> 
    </Atom> 
    </xsl:template> 

    <xsl:template match="old:Widgets" mode="single"> 
     <Molecule> 
     <xsl:apply-templates/> 
     </Molecule> 
    </xsl:template> 

    <!-- You actually only need one rule for <old:SmallWidget>. 
     Why? Because the behavior of this rule will always 
     be exactly the same as the behavior of the other rule 
     you supplied below. 
    --> 
    <xsl:template match="old:SmallWidget"> <!--[following-sibling::old:Widgets]">--> 
     <Widgets> 
         <!-- "." means exactly the same thing as "self::node()" --> 
     <xsl:apply-templates select="." mode="single"/> 

     <!-- If the node-set is empty, then this will be a no-op anyway, 
      so it's safe to have it here even for the case when 
      <old:Widgets> is not present in the source tree. --> 
            <!-- This XPath expression ensures 
             that you only process the next 
             sibling element - and then only 
             if it's name is <old:Widgets>. 
             Your schema might not allow it, 
             but this is a clearer communication 
             of your intention, and it will also 
             work correctly if another 
             old:SmallWidget/old:Widget pair 
             appeared later in the document. 
            --> 
     <xsl:apply-templates select="following-sibling::*[1][self::old:Widgets]" 
          mode="single"/> 
     </Widgets> 
    </xsl:template> 

            <!-- updated this predicate for the 
             same reason as above. Answers the 
             question: Is the element right before 
             this one a SmallWidget? (as opposed to: 
             Are there any SmallWidget elements 
             before this one?) --> 
    <xsl:template match="old:Widgets[preceding-sibling::*[1][self::old:SmallWidget]]"/> 

    <!-- Removed, because this rule effectively has the same behavior as the other one above 
    <xsl:template match="old:SmallWidget[not(following-sibling::old:Widgets)]"> 
     <Widgets> 
     <xsl:apply-templates select="self::node()" mode="single"/> 
     </Widgets> 
    </xsl:template> 
    --> 

    <!-- no need for the predicate. The format of this pattern (just a name) 
     causes this template rule's priority to be 0. Your other rule 
     for <old:Widgets> above has priority of .5, which means that it 
     will override this one automatically. You don't need to repeat 
     the constraint. Alternatively, you could keep this predicate 
     and remove the other one. Either way it will work. (It's probably 
     a good idea to place these rules next to each other though, 
     so you can read it like an if/else statement) --> 
    <xsl:template match="old:Widgets"> <!--[not(preceding-sibling::*[1][self::old:SmallWidget])]">--> 
     <Widgets> 
     <xsl:apply-templates select="." mode="single"/> 
     </Widgets> 
    </xsl:template> 

</xsl:stylesheet> 
+0

SmallWidgetとウィジェットの両方がソーススキーマのオプション要素の場合はどうなりますか? – lesscode

+0

私はあなたの最終的な解決策がこのシナリオをすでに処理しているのを見ています。私は編集した答えにあなたのコードを追加し、あなたがそれを単純化するのに役立つように変更を加えました。 –

+0

はい、ずっと簡単です。私のXSLスキルを傷つけるのを助けてくれてありがとう... – lesscode

関連する問題