3

I'm new to XSLT and I can't understand why the root get processed twice (at least this is my interpretation of this output).

EDIT: (I'm using Saxon-HE with XSLT 2.0) but also tested with several online processes, getting always the same result.

XSLT file

<?xml version="1.0" encoding="UTF-8"?>
<!-- XResume.xsl: resume.xml ==> resume.xhtml -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="https://github.com/IME-SE8/XResume">

<xsl:output method="html"/>

<xsl:template match="/">

<html>
<head>
  <meta charset="utf-8" />
  <meta lang="en" />
  <meta name="description" content="Personal Resume and Portfolio" />
  <title><xsl:value-of select="resume/personalInformation/name/attribute::shortForm" /> Website</title>
</head>
<body>
  <xsl:apply-templates select="resume"/>
</body>
</html>

</xsl:template>

<xsl:template match="resume">
  <div class="resume">

    <div class="header">
      <div class="name"><xsl:value-of select="personalInformation/name" /></div>
      <div class="contacts">
        <xsl:for-each select="personalInformation/contact">
          <div class="contactInformation">
            <p><xsl:value-of select="organization" /></p>
            <p><xsl:value-of select="address" /></p>
            <p><xsl:value-of select="phoneNumber" /></p>
            <p><xsl:value-of select="email" /></p>
          </div>
        </xsl:for-each>
      </div>
    </div>

    <div class="sections">
      <xsl:apply-templates />
    </div>
  </div>
</xsl:template>


<xsl:template match="interests"></xsl:template>
<xsl:template match="education"></xsl:template>
<xsl:template match="skills"></xsl:template>
<xsl:template match="experiences"></xsl:template>
<xsl:template match="projects"></xsl:template>
<xsl:template match="awards"></xsl:template>

</xsl:stylesheet>

XML file

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl"
href="https://github.com/IME-SE8/XResume/master/XResume.xsl"?>
<resume 
xmlns="https://github.com/IME-SE8/XResume" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/IME-SE8/XResume XResume.xsd">

    <personalInformation>
        <name first="John" last="Doe" shortForm="JD">John Doe</name>
        <contact type="institutional">
            <organization>StackOverflow Institute of Technology</organization>
            <address>Internet</address>
            <phoneNumber>+1 (666) 666-9999</phoneNumber>
            <email>john@d.oe</email>
        </contact>
    </personalInformation>


    <interests>
        <interest>Q and A</interest>
        <interest>XSLT</interest>
    </interests>

    <education></education>

    <skills></skills>

    <experiences></experiences>

    <projects></projects>

    <awards></awards>

</resume>

HTML output

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <meta charset="utf-8">
      <meta lang="en">
      <meta name="description" content="Personal Resume and Portfolio">
      <title>JD Website</title>
   </head>
   <body>
      <div class="resume">
         <div class="header">
            <div class="name">John Doe</div>
            <div class="contacts">
               <div class="contactInformation">
                  <p>StackOverflow Institute of Technology</p>
                  <p>Internet</p>
                  <p>+1 (666) 666-9999</p>
                  <p>john@d.oe</p>
               </div>
            </div>
         </div>
         <div class="sections">


                    John Doe

                        StackOverflow Institute of Technology
                        Internet
                        +1 (666) 666-9999
                        john@d.oe

















         </div>
      </div>
   </body>
</html>

(yes, with that amount of blank lines)

The output header div is perfectly fine, but inside the sections div that apply-templates renders all the information in the div header again but without the HTML tags.

Is there any XSLT processing detail am I missing? Does the template match sets the context in a way that the matched element is now considered a root or something like that?

villasv
  • 6,304
  • 2
  • 44
  • 78
  • 1
    It's going to be extremely difficult for us to help you unless you show the input XML as well. What do you mean by "processes the root twice"? If that were the case you'd have two complete `...` blocks in the output. – Jim Garrison Oct 13 '15 at 04:20
  • I'll add a MWE XML. But why is there no `` twice is also a mistery to me. – villasv Oct 13 '15 at 04:25
  • What makes you say it processed the root twice? Also, edit your output to show EXACTLY what is output. We cannot tell what is real output and what is your editorial comment (i.e. `name name name name ... info ...`). – Jim Garrison Oct 13 '15 at 04:27
  • It is just a wild guess, but something is surely being processed twice. Might be the root or the personalInformation or something else, but the name and the contact info has no obvious reason to be in the output twice to me. – villasv Oct 13 '15 at 04:31
  • Another possibility I think of is that the default behavior for processing children nodes is just outputting text. If that is the reason, I'm not sure how to deal with that. – villasv Oct 13 '15 at 04:35
  • 1
    It is. If you apply templates and there's no template defined the default is to output the text content (including all those blank lines in the input). You're on the right track, but unfortunately I don't have time to write an answer. I hope one of the XSLT experts can chime in here, because you're headed in the right direction. A hint: a template is applied in a "context", and all relative XPATH references inside that template are relative to the context node. Think about this carefully with respect to your template and you'll see what needs to change. – Jim Garrison Oct 13 '15 at 04:50
  • Can you please show your expected html output? – Sean B. Durkin Oct 13 '15 at 06:06
  • @JimGarrison It appears to me that the problem was indeed the default behavior of outputting children text (confirmed through setting the default to nothing). Since none of the other answers touched the real issue, I would accept yours. If you don't want to, I can write it myself for further reference. – villasv Oct 16 '15 at 20:20

3 Answers3

1

The problem is here:

<div class="sections">
  <xsl:apply-templates />
</div>

This applies templates to all child nodes of the current node (resume), including the personalInformation element.

As there is no matching template specified for personalInformation, the builtin XSLT templates are used by the XSLT processor and applying them results in outputting the concatenation of all descendent text-nodes of the personalInformation element.

Solution:

Replace:

<div class="sections">
  <xsl:apply-templates />
</div>

with:

<div class="sections">
  <xsl:apply-templates select="*[not(self::personalInformation)]" />
</div>

The result of the transformation now doesn't contain the noted problematic output:

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <meta charset="utf-8">
      <meta lang="en">
      <meta name="description" content="Personal Resume and Portfolio">
      <title>JD Website</title>
   </head>
   <body>
      <div class="resume">
         <div class="header">
            <div class="name">John Doe</div>
            <div class="contacts">
               <div class="contactInformation">
                  <p>StackOverflow Institute of Technology</p>
                  <p>Internet</p>
                  <p>+1 (666) 666-9999</p>
                  <p>john@d.oe</p>
               </div>
            </div>
         </div>
         <div class="sections"></div>
      </div>
   </body>
</html>
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
0

You haven't supplied an expected output, so I will guess at your intended outcome. Here is an XSLT 2.0 solution. If you need XSLT 1.0, please comment, and I can add. But just remember that if your transform engine is the browser, you have no excuse not to use XSLT 2.0. (Refer Saxon CE).

XSLT 2.0 Solution

This XSLT 2.0 stylesheet ...

<xsl:transform
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:r="https://github.com/IME-SE8/XResume" 
    exclude-result-prefixes="r"
    version="2.0">

<xsl:output method="html" version="5" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />

<xsl:template match="/">
  <html>
    <head>
      <meta lang="en" />
      <meta name="description" content="Personal Resume and Portfolio" />
      <title><xsl:value-of select="r:resume/r:personalInformation/r:name/@shortForm" /> Website</title>
    </head>
    <body>
      <xsl:apply-templates select="r:resume"/>
    </body>
  </html>
</xsl:template>

<xsl:template match="r:resume">
  <div class="resume">
    <div class="header">
      <div class="name"><xsl:value-of select="r:personalInformation/r:name" /></div>
      <div class="contacts">
        <xsl:apply-templates select="r:personalInformation/r:contact" />
      </div>
    </div>
    <div class="sections">
      <xsl:apply-templates select="* except r:personalInformation" />
    </div>
  </div>      
</xsl:template>

<xsl:template match="r:contact">
  <div class="contactInformation">
    <xsl:apply-templates />
  </div>
</xsl:template>

<xsl:template match="r:organization|r:address|r:phoneNumber|r:email">
  <p><xsl:value-of select="." /></p>
</xsl:template>

<xsl:template match="r:education|r:skills|r:experiences|r:projects|r:awards">
  <h2><xsl:value-of select="local-name()" /></h2>
  <p><xsl:value-of select="." /></p>
</xsl:template>

<xsl:template match="r:interests">
  <h2>interests</h2>
  <ul>
    <xsl:apply-templates />
  </ul>
</xsl:template>

<xsl:template match="r:interest">
  <li>
    <xsl:value-of select="." />
  </li>
</xsl:template>

<xsl:template match="*" />

</xsl:transform>

... when applied to this input document ...

<resume 
    xmlns="https://github.com/IME-SE8/XResume" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://github.com/IME-SE8/XResume XResume.xsd">

    <personalInformation>
        <name first="John" last="Doe" shortForm="JD">John Doe</name>
        <contact type="institutional">
            <organization>StackOverflow Institute of Technology</organization>
            <address>Internet</address>
            <phoneNumber>+1 (666) 666-9999</phoneNumber>
            <email>john@d.oe</email>
        </contact>
    </personalInformation>


    <interests>
        <interest>Q and A</interest>
        <interest>XSLT</interest>
    </interests>

    <education></education>

    <skills></skills>

    <experiences></experiences>

    <projects></projects>

    <awards></awards>

</resume>

... will yield this output html page ....

<!DOCTYPE HTML>
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <meta lang="en">
      <meta name="description" content="Personal Resume and Portfolio">
      <title>JD Website</title>
   </head>
   <body>
      <div class="resume">
         <div class="header">
            <div class="name">John Doe</div>
            <div class="contacts">
               <div class="contactInformation">
                  <p>StackOverflow Institute of Technology</p>
                  <p>Internet</p>
                  <p>+1 (666) 666-9999</p>
                  <p>john@d.oe</p>
               </div>
            </div>
         </div>
         <div class="sections">
            <h2>interests</h2>
            <ul>
               <li>Q and A</li>
               <li>XSLT</li>
            </ul>
            <h2>education</h2>
            <p></p>
            <h2>skills</h2>
            <p></p>
            <h2>experiences</h2>
            <p></p>
            <h2>projects</h2>
            <p></p>
            <h2>awards</h2>
            <p></p>
         </div>
      </div>
   </body>
</html>

Explanation: Why were you getting root processed twice

In short because your <div class="sections"><xsl:apply-templates /></div> instruction did not specify a select attribute. The default selection applied, which was at that point the document root.

Sean B. Durkin
  • 12,659
  • 1
  • 36
  • 65
0

All the elements in your source document are in a namespace, but your stylesheet is written to process elements in no namespace. Welcome to the club, and join the 10m other people who have fallen into this trap. Essentially, you resume elements don't match the match="resume" template, so the default template kicks in, and this outputs the raw text with no tags. For the solution, search on "XSLT default namespace" and choose any one of about 1000 answers.

On re-reading, I see that you've used xpath-default-namespace="https://github.com/IME-SE8/XResume", which should fix the problem if you are using an XSLT 2.0 processor, or trigger an error if you're using an XSLT 1.0 processor. So it might be useful (actually, it's always useful) to tell us what processor you are using and how you are running it.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164