Domanda

I'm new to XSLT -- I'm trying to utilize this to recreate (copy) an existing xml document based on a specified attribute.

As an example, with below XML, I want to get the lesson that has the attribute tags="6" from the mainGroup that has the attribute of group="Book1"

<Groups>
<mainGroup id="1" group="Book1">
<subGroup name="Chapter 1">
    <lesson name="Lesson1" tags="1" />
    <lesson name="Lesson2" tags="2" />
</subGroup>
<subGroup name="Chapter 2">
    <lesson name="Lesson1" tags="3" />
    <lesson name="Lesson2" tags="4" />
</subGroup>
<subGroup name="Chapter 3">
    <subGroup name="Chapter 3 Examples">
        <lesson name="Lesson2" tags="5" />
    </subGroup>
    <lesson name="Lesson1" tags="6" />
</subGroup>
</mainGroup>
<mainGroup id="1" group="Book2">
<subGroup name="Chapter 1">
    <lesson name="Lesson1" tags="1" />
    <lesson name="Lesson2" tags="2" />
</subGroup>
<subGroup name="Chapter 2">
    <lesson name="Lesson1" tags="3" />
    <lesson name="Lesson2" tags="4" />
</subGroup>
<subGroup name="Chapter 3">
    <subGroup name="Chapter 3 Examples">
        <lesson name="Lesson2" tags="6" />
    </subGroup>
    <lesson name="Lesson1" tags="5" />
</subGroup>
</mainGroup>
</Groups>

with an expected (anticipated) result of the below after XSL is applied

<Groups>
<mainGroup id="1" group="Book1">
<subGroup name="Chapter 3">
    <lesson name="Lesson1" tags="6" />
</subGroup>
</mainGroup>
 </Groups>

As far as the XSL, what I've come up with so far and the closest I've gotten is:

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


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


// Remove all maingroups that are not book1
 <xsl:template match="mainGroup[not(@group='Book1')]" />

// Remove all lessons that do not have tags of 5
 <xsl:template match="lesson[not(@tags='5')]" />

</xsl:stylesheet>

which gives me the XML result of:

<Groups>
   <mainGroup id="1" group="Book1">
      <subGroup name="Chapter 1" />
      <subGroup name="Chapter 2" />
      <subGroup name="Chapter 3">
         <subGroup name="Chapter 3 Examples">
            <lesson name="Lesson2" tags="5" />
         </subGroup>
      </subGroup>
   </mainGroup>
</Groups>

I cannot figure out how to now remove the empty subGroups --

Some things I've tried (again please don't laugh - new at this XSL :) )

Hoping this removed those that didn't have a lesson node - but didn't seem to do anything

 <xsl:template match="subGroup[count(lesson) = 0]" />

 <xsl:template match="subGroup[not(node())]" />

and even something like this ---

<xsl:template match="subGroups" mode="copy">
     <xsl:choose>
       <xsl:when test="count(./*)">
         <xsl:copy>
           <xsl:apply-templates select="@*|node()" mode="copy"/>
         </xsl:copy>
       </xsl:when>
       <xsl:otherwise>
       </xsl:otherwise>
     </xsl:choose>
   </xsl:template>

but after hours of no success I'm throwing in the towel - I'm sure someone here probably reading this is thinking -- ahhh, you just need to add or do this -- so any help would be appreciated.

Thanks

È stato utile?

Soluzione

The key thing is that template rules match against the input XML, so you have to suppress those subGroup elements that don't contain the lesson you want:

<xsl:template match="subGroup[not(.//lesson/@tags = '5')]"/>

For cases where the rules on which subgroups will ultimately end up empty are more complex it may be easier to do a two stage transformation, with a first pass as you're currently doing and then a second pass that processes the results of the first to strip out the remaining empty tags. In XSLT 2.0 this is straightforward

<xsl:variable name="pass1">
  <xsl:apply-templates />
</xsl:variable>
<xsl:apply-templates select="$pass1" mode="pass2"/>

But in 1.0 it requires a node-set extension function. You can find plenty of examples of this technique in other questions if you want to go that route.

Altri suggerimenti

Instead of buying everything wholesale, then trying to get rid of the stuff you don't need, why don't you just pick what you do need in the first place?

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/Groups">
    <xsl:copy>
        <xsl:apply-templates select="mainGroup[@group='Book1']"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="mainGroup">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates select="subGroup[lesson[@tags='6']]"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="subGroup">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:copy-of select="lesson[@tags='6']"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Or, if you prefer:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/">
    <Groups>
        <xsl:for-each select="Groups/mainGroup[@group='Book1']/subGroup/lesson[@tags='6']">
            <mainGroup>
                <xsl:copy-of select="../../@*"/>
                <subGroup>
                    <xsl:copy-of select="../@*"/>
                    <xsl:copy-of select="."/>
                </subGroup>
            </mainGroup>
        </xsl:for-each>
    </Groups>
</xsl:template>

</xsl:stylesheet>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top