良いXSLTソリューションは、単純なテンプレートルールにあなたの人間が読めるルールをマッピングします。ここでは、あなたの言葉で、ルールです:specAで
<SmallWidget>
はspecBで<Atom>
と同じことを意味し、これだけの要素の名前を変更します。
<Widgets>
はspecBで<Molecule>
と同じことを意味していますので、要素の名前を変更してください。
- という名前の要素には、
<Atom>
と<Molecule>
が含まれています。これは、specAの<Widgets>
とは異なる意味です。
- 他のものはすべてそのままコピーされますが、新しい名前空間にコピーされます。
さんはそれを行くを挙げてみましょう:それはあなたが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>
私は任意の要素のサポートを含むようにソリューションを変更しました。 "http://widgetspecA.com/ns"以外の名前空間にあるノードはそのままコピーされ、名前空間はそのまま残されます。 – Tomalak
これは一見動いているターゲットなので、私は答えを削除しました。次回は完全で曖昧ではない問題の説明を投稿してください。 – Tomalak
Wayne、あなたの最終的な解決策には良い仕事。それはまだ簡素化することができます。私の編集された答えを見てください。 –