0

Learning XSLT, and I would like to know if this transformation is possible

Input something like this

<book>
  <title>Title 1</title>
  <author>Author 1</author>
</book>
<book>
  <title>Title 2</title>
  <author>Author 2</author>
</book>
<book>
  <title>Title 3</title>
  <author>Author 3</author>
</book>

Output

<book1>
  <title1>Title 1</title1>
  <author1>Author 1</author1>
</book1>
<book2>
  <title2>Title 2</title2>
  <author2>Author 2</author2>
</book2>
<book3>
  <title3>Title 3</title3>
  <author3>Author 3</author3>
</book3>

What would the XSLT for this look like?

3
  • Maybe this can help? stackoverflow.com/questions/833118/… Commented Dec 18, 2013 at 17:34
  • 1
    Why don't you take a look at the <xsl:element> instruction that allows you to create an element with a calculated name, and at the position() function? Commented Dec 18, 2013 at 17:36
  • 6
    It's possible but it's probably a bad idea - if you have elements that represent the same kind of data then they should have the same name, you're just making more work for the downstream consumers of the XML. Commented Dec 18, 2013 at 17:51

2 Answers 2

1

As stated in the comments this is quite easy, but ill advised. However, if you acknowledge that it is ill-advised, then there is no reason to show how.

Firstly, in XSLT there are two ways to create an element, by placing the element inline:

<xsl:template match="book">
    <book1>
        <!-- Some stuff -->
    </book1>
<xsl:template>

Or programatically using the xsl:element directive:

<xsl:template match="book">
    <xsl:element name="book1">
        <!-- Some stuff -->
    </xsl:element>
<xsl:template>

The @name attribute in the above code can take an XPath expression if we use an attribute value template. Which means we can insert a dynamic value in there:

<xsl:template match="book">
    <xsl:element name="book{//some/xpath/here()}">
        <!-- Some stuff -->
    </xsl:element>
<xsl:template>

Additionally, we can get the position of the current node based on the current context using the position() Xpath function:

<xsl:template match="/">
    <xsl:for-each select="//book">
        <!-- this loop will number the books in the whole document 
             because its seaching in all nodes (//). -->
        <xsl:value-of select="position()"/>
    </xsl:for-each>
<xsl:template>

<xsl:template match="library">
    <xsl:for-each select=".//book">
        <!-- this loop will number the books in the current *library*
             because its seaching in all nodes under the library node (.//)  -->
        <xsl:value-of select="position()"/>
    </xsl:for-each>
<xsl:template>

As Michael Kay showed in his answer as I was typing this (grrr), combining these is trivial.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. That makes a lot of sense.
0

I've no idea why you would want to create such a horrible XML document, but if you must, you can do it using:

<xsl:template match="book">
  <xsl:variable name="n" select="position()"/>
  <xsl:element name="book{$n}">
    <xsl:element name="title{$n}">
      <xsl:value-of select="title"/>
    </xsl:element>
    <xsl:element name="author{$n}">
      <xsl:value-of select="author"/>
    </xsl:element>
  </xsl:element>
</xsl:template>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.