Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. XML / XSL
  4. xsl:apply-templates help please! [modified]

xsl:apply-templates help please! [modified]

Scheduled Pinned Locked Moved XML / XSL
xmlhelpwpftutorialquestion
6 Posts 2 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • N Offline
    N Offline
    Neophyte30
    wrote on last edited by
    #1

    I am attempting to digest someone elses xml - which is why I can't just reformat it as I'd like. I think that applying templates (possibly nested) is the way to go, but I'm buggered if I can see how to do it properly - the w3schools site covers the basics, but not what I'm after. Can anyone a) solve my problem or b) point me to somewhere I can find a more in-depth explanation? I'd even settle for c) "XSLT does not work that way!" Source XML:

    <Beds>
    <Single>
    <Base>1</Base>
    <BaseCode>BER</BaseCode>
    <Mattress>2</Mattress>
    <MattressCode>BER</MattressCode>
    <Headboard>3</Headboard>
    <HeadboardCode>BER</HeadboardCode>
    </Single>
    <Double>
    <Base>0</Base>
    <BaseCode></BaseCode>
    <Mattress>1</Mattress>
    <MattressCode>BER</MattressCode>
    <Headboard>2</Headboard>
    <HeadboardCode>TBD</HeadboardCode>
    </Double>
    <King>
    <Base></Base>
    <BaseCode></BaseCode>
    <Mattress></Mattress>
    <MattressCode></MattressCode>
    <Headboard></Headboard>
    <HeadboardCode></HeadboardCode>
    </King>
    <Sofa>
    <Foam>5</Foam>
    <FoamCode>BER</FoamCode>
    <Metal></Metal>
    <MetalCode></MetalCode>
    </Sofa>
    </Beds>

    Desired Output XML:

    <Row>
    <Description>SingleBase</Description>
    <Value>BER</Value>
    <Quantity>1</Quantity>
    </Row>
    <Row>
    <Description>SingleMattress</Description>
    <Value>BER</Value>
    <Quantity>2</Quantity>
    </Row>
    <Row>
    <Description>SingleHeadboard</Description>
    <Value>BER</Value>
    <Quantity>3</Quantity>
    </Row>
    <Row>
    <Description>DoubleMattress</Description>
    <Value>BER</Value>
    <Quantity>1</Quantity>
    </Row>
    <Row>
    <Description>DoubleHeadboard</Description>
    <Value>BER</Value>
    <Quantity>2</Quantity>
    </Row>
    <Row>
    <Description>SofaFoam</Description>
    <Value>BER</Value>
    <Quantity>5</Quantity>
    </Row>

    I thought I'd start with:

    <xsl:for-each select="Beds">
    <Row>
    xsl:apply-templates</xsl:apply-templat

    N S 2 Replies Last reply
    0
    • N Neophyte30

      I am attempting to digest someone elses xml - which is why I can't just reformat it as I'd like. I think that applying templates (possibly nested) is the way to go, but I'm buggered if I can see how to do it properly - the w3schools site covers the basics, but not what I'm after. Can anyone a) solve my problem or b) point me to somewhere I can find a more in-depth explanation? I'd even settle for c) "XSLT does not work that way!" Source XML:

      <Beds>
      <Single>
      <Base>1</Base>
      <BaseCode>BER</BaseCode>
      <Mattress>2</Mattress>
      <MattressCode>BER</MattressCode>
      <Headboard>3</Headboard>
      <HeadboardCode>BER</HeadboardCode>
      </Single>
      <Double>
      <Base>0</Base>
      <BaseCode></BaseCode>
      <Mattress>1</Mattress>
      <MattressCode>BER</MattressCode>
      <Headboard>2</Headboard>
      <HeadboardCode>TBD</HeadboardCode>
      </Double>
      <King>
      <Base></Base>
      <BaseCode></BaseCode>
      <Mattress></Mattress>
      <MattressCode></MattressCode>
      <Headboard></Headboard>
      <HeadboardCode></HeadboardCode>
      </King>
      <Sofa>
      <Foam>5</Foam>
      <FoamCode>BER</FoamCode>
      <Metal></Metal>
      <MetalCode></MetalCode>
      </Sofa>
      </Beds>

      Desired Output XML:

      <Row>
      <Description>SingleBase</Description>
      <Value>BER</Value>
      <Quantity>1</Quantity>
      </Row>
      <Row>
      <Description>SingleMattress</Description>
      <Value>BER</Value>
      <Quantity>2</Quantity>
      </Row>
      <Row>
      <Description>SingleHeadboard</Description>
      <Value>BER</Value>
      <Quantity>3</Quantity>
      </Row>
      <Row>
      <Description>DoubleMattress</Description>
      <Value>BER</Value>
      <Quantity>1</Quantity>
      </Row>
      <Row>
      <Description>DoubleHeadboard</Description>
      <Value>BER</Value>
      <Quantity>2</Quantity>
      </Row>
      <Row>
      <Description>SofaFoam</Description>
      <Value>BER</Value>
      <Quantity>5</Quantity>
      </Row>

      I thought I'd start with:

      <xsl:for-each select="Beds">
      <Row>
      xsl:apply-templates</xsl:apply-templat

      N Offline
      N Offline
      Neophyte30
      wrote on last edited by
      #2

      OK, I have:

      Can anyone point me toward the proper XPath expression to say "sibling that begins with the name of the current element [and ends with 'Code']"? I'd like to sidestep the if, if that's possible...

      Thanks,

      Al

      S 1 Reply Last reply
      0
      • N Neophyte30

        I am attempting to digest someone elses xml - which is why I can't just reformat it as I'd like. I think that applying templates (possibly nested) is the way to go, but I'm buggered if I can see how to do it properly - the w3schools site covers the basics, but not what I'm after. Can anyone a) solve my problem or b) point me to somewhere I can find a more in-depth explanation? I'd even settle for c) "XSLT does not work that way!" Source XML:

        <Beds>
        <Single>
        <Base>1</Base>
        <BaseCode>BER</BaseCode>
        <Mattress>2</Mattress>
        <MattressCode>BER</MattressCode>
        <Headboard>3</Headboard>
        <HeadboardCode>BER</HeadboardCode>
        </Single>
        <Double>
        <Base>0</Base>
        <BaseCode></BaseCode>
        <Mattress>1</Mattress>
        <MattressCode>BER</MattressCode>
        <Headboard>2</Headboard>
        <HeadboardCode>TBD</HeadboardCode>
        </Double>
        <King>
        <Base></Base>
        <BaseCode></BaseCode>
        <Mattress></Mattress>
        <MattressCode></MattressCode>
        <Headboard></Headboard>
        <HeadboardCode></HeadboardCode>
        </King>
        <Sofa>
        <Foam>5</Foam>
        <FoamCode>BER</FoamCode>
        <Metal></Metal>
        <MetalCode></MetalCode>
        </Sofa>
        </Beds>

        Desired Output XML:

        <Row>
        <Description>SingleBase</Description>
        <Value>BER</Value>
        <Quantity>1</Quantity>
        </Row>
        <Row>
        <Description>SingleMattress</Description>
        <Value>BER</Value>
        <Quantity>2</Quantity>
        </Row>
        <Row>
        <Description>SingleHeadboard</Description>
        <Value>BER</Value>
        <Quantity>3</Quantity>
        </Row>
        <Row>
        <Description>DoubleMattress</Description>
        <Value>BER</Value>
        <Quantity>1</Quantity>
        </Row>
        <Row>
        <Description>DoubleHeadboard</Description>
        <Value>BER</Value>
        <Quantity>2</Quantity>
        </Row>
        <Row>
        <Description>SofaFoam</Description>
        <Value>BER</Value>
        <Quantity>5</Quantity>
        </Row>

        I thought I'd start with:

        <xsl:for-each select="Beds">
        <Row>
        xsl:apply-templates</xsl:apply-templat

        S Offline
        S Offline
        Stuart Dootson
        wrote on last edited by
        #3

        use the 'name()' function to access a node name The following XSLT should do what you want...

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

        <xsl:output method="xml"/>

        <xsl:template match="/">
        <table>
        <!-- Iterate through all bed types and items -->
        <xsl:apply-templates select="Beds/*/*"/>
        </table>
        </xsl:template>

        <!-- Just process the odd items (1st, 3rd, 5th etc) for each bed type -->
        <xsl:template match="Beds/*/*[(position() mod 2) = 1]">
        <xsl:if test="text() and following-sibling::*[1]/text()">
        <Row>
        <!-- Description is the parent name + this node's name -->
        <Description><xsl:value-of select="concat(name(parent::node()), name())"/></Description>
        <!-- Value is just the next sibling node's text -->
        <Value><xsl:value-of select="following-sibling::*[1]/text()"/></Value>
        <!-- Quantity is this node's text -->
        <Quantity><xsl:value-of select="text()"/></Quantity>
        </Row>
        </xsl:if>
        </xsl:template>

        <!-- This template just sucks up the even items -->
        <xsl:template match="*"/>

        </xsl:stylesheet>

        Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p CodeProject MVP for 2010 - who'd'a thunk it!

        N 1 Reply Last reply
        0
        • N Neophyte30

          OK, I have:

          Can anyone point me toward the proper XPath expression to say "sibling that begins with the name of the current element [and ends with 'Code']"? I'd like to sidestep the if, if that's possible...

          Thanks,

          Al

          S Offline
          S Offline
          Stuart Dootson
          wrote on last edited by
          #4

          Neophyte30 wrote:

          Can anyone point me toward the proper XPath expression to say "sibling that begins with the name of the current element [and ends with 'Code']"? I'd like to sidestep the if, if that's possible...

          I'd try something like:

          ../*[name() = concat(name(current()), 'Base')]

          Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p CodeProject MVP for 2010 - who'd'a thunk it!

          1 Reply Last reply
          0
          • S Stuart Dootson

            use the 'name()' function to access a node name The following XSLT should do what you want...

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

            <xsl:output method="xml"/>

            <xsl:template match="/">
            <table>
            <!-- Iterate through all bed types and items -->
            <xsl:apply-templates select="Beds/*/*"/>
            </table>
            </xsl:template>

            <!-- Just process the odd items (1st, 3rd, 5th etc) for each bed type -->
            <xsl:template match="Beds/*/*[(position() mod 2) = 1]">
            <xsl:if test="text() and following-sibling::*[1]/text()">
            <Row>
            <!-- Description is the parent name + this node's name -->
            <Description><xsl:value-of select="concat(name(parent::node()), name())"/></Description>
            <!-- Value is just the next sibling node's text -->
            <Value><xsl:value-of select="following-sibling::*[1]/text()"/></Value>
            <!-- Quantity is this node's text -->
            <Quantity><xsl:value-of select="text()"/></Quantity>
            </Row>
            </xsl:if>
            </xsl:template>

            <!-- This template just sucks up the even items -->
            <xsl:template match="*"/>

            </xsl:stylesheet>

            Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p CodeProject MVP for 2010 - who'd'a thunk it!

            N Offline
            N Offline
            Neophyte30
            wrote on last edited by
            #5

            Thanks for this - I had found the "following-sibling" axis on a tutorial page somewhere which led me to:

            <xsl:for-each select="Beds/*/*">
            xsl:choose
            <xsl:when test="not(contains(name(.),'Code')) and .>0">
            <Row>
            <Description>
            <xsl:value-of select="name(..)"/>
            <xsl:value-of select="name(.)"/>
            </Description>
            <Value>
            <xsl:value-of select="following-sibling::*[contains(name(),name(.))]"/>
            </Value>
            <Quantity>
            <xsl:value-of select="."/>
            </Quantity>
            </Row>
            </xsl:when>
            </xsl:choose>
            </xsl:for-each>

            Your solution demonstrates to me that I have hardly scratched the surface of XPath, and that there are many ways of achieving the same result! Is there a performance (or other) benefit to using templates apart from the feeling of doing things properly as opposed to my schonky approach? Can you recommend any sites or good books to get a deeper insight into XPath/XSLT? Thanks again for your time, Al Edit--- My use of for-each amply demonstrates the adage that when all you have is a hammer...

            S 1 Reply Last reply
            0
            • N Neophyte30

              Thanks for this - I had found the "following-sibling" axis on a tutorial page somewhere which led me to:

              <xsl:for-each select="Beds/*/*">
              xsl:choose
              <xsl:when test="not(contains(name(.),'Code')) and .>0">
              <Row>
              <Description>
              <xsl:value-of select="name(..)"/>
              <xsl:value-of select="name(.)"/>
              </Description>
              <Value>
              <xsl:value-of select="following-sibling::*[contains(name(),name(.))]"/>
              </Value>
              <Quantity>
              <xsl:value-of select="."/>
              </Quantity>
              </Row>
              </xsl:when>
              </xsl:choose>
              </xsl:for-each>

              Your solution demonstrates to me that I have hardly scratched the surface of XPath, and that there are many ways of achieving the same result! Is there a performance (or other) benefit to using templates apart from the feeling of doing things properly as opposed to my schonky approach? Can you recommend any sites or good books to get a deeper insight into XPath/XSLT? Thanks again for your time, Al Edit--- My use of for-each amply demonstrates the adage that when all you have is a hammer...

              S Offline
              S Offline
              Stuart Dootson
              wrote on last edited by
              #6

              Hi Al…thought I'd replied to this, but I guess I killed my browser without pressing Post…doh! 1. apply-templates will very likely have better performance than for-each - there's more scope for the XSLT processor to do optimisations, as apply-templates is a more abstract operation. 2. I've used Michael Kay's XSLT and XPath books, but I think Jeni Tennison's Beginning XSLT would be appropriate for what you want...

              Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p CodeProject MVP for 2010 - who'd'a thunk it!

              1 Reply Last reply
              0
              Reply
              • Reply as topic
              Log in to reply
              • Oldest to Newest
              • Newest to Oldest
              • Most Votes


              • Login

              • Don't have an account? Register

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • World
              • Users
              • Groups