2011-06-23 11 views
8

Po wypróbowaniu przez ponad 8 godzin, mam nadzieję, że ktoś może mi pomóc z tym:Jak to przodek gałąź węzła wybranego przez parametr XPath w wyjściu XSLT

otrzymuje następujące (uproszczony) XML do książki:

<book> 
    <section name="A"> 
     <chapter name="I"> 
      <paragraph name="1"/> 
      <paragraph name="2"/> 
     </chapter> 
     <chapter name="II"> 
      <paragraph name="1"/>   
     </chapter> 
    </section> 
    <section name="B"> 
     <chapter name="III"/> 
     <chapter name="IV"/> 
    </section> 
</book> 

Jestem w stanie wyodrębnić każdą część (rozdział, rozdział lub akapit) z książki opartej na XML danego parametru z następującym xsl:

<xsl:param name="subSelectionXPath" required="yes" as="node()"/> 

<xsl:template match="/"> 
    <xsl:apply-templates select="$subSelectionXPath"/> 
</xsl:template> 

<xsl:template match="*"> 
    <!-- output node with all children --> 
    <xsl:copy-of select="."/> 
</xsl:template> 

a parametr $ subSelectionXPath z wartością jak

doc(filename)//chapter[@name='II']

powodując wyjścia:

<chapter name="II"> 
    <paragraph name="1"/>   
</chapter> 

co chcę osiągnąć dodatkowo ma mieć wybrany fragment XML otoczone przodek XML oddziału, tj:

<book> 
    <section name="A"> 
     <chapter name="II"> 
      <paragraph name="1"/>   
     </chapter> 
    </section>  
</book> 

sobie wyobrazić (i próbował) przemierza drzewo XML i testowania, czy bieżący węzeł jest przodkiem, coś jak (pseudo kod):

<xsl:if test="node() in $subSelectionXPath/ancestor::node()"> 
    <xsl:copy> 
     <xsl:apply-templates/> 
    </xsl:copy> 
</xsl:if> 

Eksperymentowałem również z xsl: key, ale obawiam się, że moja wiedza na temat XSLT dobiega końca. jakieś pomysły?

+0

więc, biorąc pod uwagę element wejściowy, chcesz skopiować z powrotem do jego przodków i wyjście elementu z przodków, bez rodzeństwa, mam rację? –

+1

Dobre pytanie, +1. Zobacz moją odpowiedź na kompletne, krótkie i łatwe rozwiązanie XSLT 2.0. –

+1

Dodałem także szczegółowe wyjaśnienie. –

Odpowiedz

6

Z twojego kodu wynika, że ​​używasz XSLT 2.0.

Ten XSLT 2.0 transformacja:

<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:param name="subSelectionXPath" 
    as="node()" select="//chapter[@name='II']" 
    /> 

<xsl:template match="*[descendant::node() intersect $subSelectionXPath]"> 
    <xsl:copy> 
    <xsl:copy-of select="@*"/> 
    <xsl:apply-templates select="*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="*[. intersect $subSelectionXPath]"> 
    <xsl:copy-of select="."/> 
</xsl:template> 
</xsl:stylesheet> 

po naniesieniu na dostarczonym dokumencie XML:

<book> 
    <section name="A"> 
     <chapter name="I"> 
      <paragraph name="1"/> 
      <paragraph name="2"/> 
     </chapter> 
     <chapter name="II"> 
      <paragraph name="1"/> 
     </chapter> 
    </section> 
    <section name="B"> 
     <chapter name="III"/> 
     <chapter name="IV"/> 
    </section> 
</book> 

produkuje dokładnie Wanted, poprawny wynik:

<book> 
    <section name="A"> 
     <chapter name="II"> 
     <paragraph name="1"/> 
     </chapter> 
    </section> 
</book> 

Objaśnienie: My mają tylko dwa szablony:

  1. szablon, który pasuje do każdego elementu, którego potomkowie mają niepuste przecięcie z węzłem-set $subSelectionXPath. Tutaj "płytko kopiujemy" element i stosujemy szablony do jego elementów potomnych.

  2. Szablon dopasowujący elementy należące do zestawu węzłów $subSelectionXPath. Tutaj kopiujemy całe poddrzewo zakorzenione w tym elemencie.

  3. Należy zwrócić uwagę na użycie operatora XPath 2.0 intersect.

  4. No jawna rekursja.

II. XSLT 1.0 rozwiązanie:

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

<xsl:param name="subSelectionXPath" 
    select="//chapter[@name='II']" 
    /> 

<xsl:template match="*"> 
    <xsl:choose> 
    <xsl:when test= 
    "descendant::node() 
     [count(.|$subSelectionXPath) 
     = 
     count($subSelectionXPath) 
     ] 
    "> 
    <xsl:copy> 
    <xsl:copy-of select="@*"/> 
    <xsl:apply-templates select="*"/> 
    </xsl:copy> 
    </xsl:when> 

    <xsl:when test= 
    "count(.|$subSelectionXPath) 
    = 
    count($subSelectionXPath) 
    "> 
    <xsl:copy-of select="."/> 
    </xsl:when> 
    </xsl:choose> 
</xsl:template> 
</xsl:stylesheet> 

Gdy ta transformacja jest stosowane do tego samego dokumentu XML (przedstawionego już powyżej), ten sam chciał i poprawny wynik jest produkowany:

<book> 
    <section name="A"> 
     <chapter name="II"> 
     <paragraph name="1"/> 
     </chapter> 
    </section> 
</book> 

Objaśnienie: Jest to w istocie rozwiązanie XSLT 2.0, w którym operator XPath 2.0 intersect jest tłumaczony na XPath 1.0 przy użyciu znanej metody Kayessian (dla @Michael Kay) dla przecięcia dwóch zestawów węzłów $ns1 i $ns2:

$ns1[count(.|$ns2) = count($ns2)] 
+0

+1 dla obu rozwiązań. –

+0

@empo: Twoje uznanie to coś, co bardzo cenię. Dziękuję Ci. –

+0

Dzięki za odpowiedź! – Brunsaim

3

Myślę, że droga do tego, co starasz się osiągnąć to z rekurencyjnego wzoru:

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

<xsl:template match="/"> 
    <xsl:call-template name="copyElementsOnAncestorAxis"> 
     <xsl:with-param name="nodeList" 
         select="//chapter[@name='I']/ancestor-or-self::*"/> 
    </xsl:call-template> 
    </xsl:template> 

    <xsl:template name="copyElementsOnAncestorAxis"> 
    <xsl:param name="nodeList"/> 
    <xsl:choose> 
     <!-- if the context node is the last node in the list, copy it entirely --> 
     <xsl:when test=". = $nodeList[count($nodeList)]"> 
     <xsl:copy-of select="."/> 
     </xsl:when> 
     <!-- otherwise, just copy the element, its attributes, and any child element that 
      is also in the node list --> 
     <xsl:otherwise> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 
      <xsl:for-each select="*[. = $nodeList]"> 
      <xsl:call-template name="copyElementsOnAncestorAxis"> 
       <xsl:with-param name="nodeList" 
           select="$nodeList"/> 
      </xsl:call-template> 
      </xsl:for-each> 
     </xsl:copy> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:template> 

</xsl:stylesheet> 

, które po nałożeniu na XML, który podałeś, produkuje to:

<book> 
    <section name="A"> 
    <chapter name="I"> 
     <paragraph name="1" /> 
     <paragraph name="2" /> 
    </chapter> 
    </section> 
</book> 
+0

+1 bardzo ładne rozwiązanie. – Tomalak

2

To jest inne rozwiązanie oparte na rekursji. Objaśnienie:

  • najpierw zastosować-szablony do ostatniego przodka
  • wtedy, jeśli przodek obecnego węzła parametrów skopiować go i jeśli rodzic skopiować węzeł i koniec, inaczej recurse

Tutaj transformaty :

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

    <xsl:param name="subSelectionXPath" 
     select="document('test_input2.xml')//chapter[@name='II']"/> 

    <xsl:template match="/"> 
     <xsl:apply-templates 
      select="$subSelectionXPath/ancestor::*[position()=last()]"/> 
    </xsl:template> 

    <xsl:template match="*"> 
     <xsl:choose> 

      <xsl:when test="$subSelectionXPath/ancestor::* 
       [generate-id() = generate-id(current())]"> 
       <xsl:copy> 
        <xsl:copy-of select="@*"/> 

        <xsl:choose> 
         <xsl:when test="generate-id(.)= 
          generate-id($subSelectionXPath/ancestor::*[1])"> 
          <xsl:copy-of select="$subSelectionXPath"/> 
         </xsl:when> 
         <xsl:otherwise> 
          <xsl:apply-templates select="*"/> 
         </xsl:otherwise> 
        </xsl:choose> 

       </xsl:copy> 
      </xsl:when> 
      <xsl:otherwise/> 
     </xsl:choose> 
    </xsl:template> 
    </xsl:stylesheet> 

po nałożeniu na wejście podany w pytaniu, przy założeniu, że wartość parametru wejściowego samo w pytaniu, produkuje:

<book> 
    <section name="A"> 
     <chapter name="II"> 
     <paragraph name="1"/> 
     </chapter> 
    </section> 
</book> 
+0

Przepraszamy za wiele edycji, ale formatowanie doprowadza mnie do szału :) –

+0

Co ciekawe, to właśnie to rozwiązanie działa dla mnie. Dla jasności uprościłem przykład, ale w moim rzeczywistym kodzie parametr $ subSelectionXPath zaczyna się od doc (...) i dostarczam go z linii poleceń. To nie działało na kod Dimitre'a, ale działa w Empo. – Brunsaim

+0

@Brunsaim: Wszystko, czego potrzebujesz to podstawienie w moim kodzie definicji '$ subSelectionXpath' z twoją definicją - unikam używania' doc() 'lub' document() 'w moich odpowiedziach, gdy esencja w ogóle się nie zmieni . –

Powiązane problemy