0

I have been asked to convert a XML doc with an internal DTD and an external XSL into a neatly presented table with two sets of criteria - which I chose as two different authors.

TBH I don't even know if I have the XML & DTD doc right? And when it comes to using XPath and XSLT I have absolutely no idea what to do. I'm hoping someone can show me the finished table so that I can work backwards from it and understand how you did it.

Here is my XML Doc...

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="Batman.xsl"?>
<!DOCTYPE comics [
<!ELEMENT comics (name,author,publisher,country,year,prics)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT publisher (#PCDATA)>
<!ELEMENT country (#PCDATA)>
<!ELEMENT year (#PCDATA)>
<!ELEMENT price (#PCDATA)>
]>
<comics>
   <batman>
       <issue2>
           <name>Batman Eternal</name>
           <author>Scott Synder</author>
           <publisher>DC Comics</publisher>
           <country>USA</country>
           <year>2012</year>
           <price>$2.99</price>
       </issue2>
       <issue3>
           <name>Batman Eternal</name>
           <author>Scott Synder</author>
           <publisher>DC Comics</publisher>
           <country>USA</country>
           <year>2012</year>
           <price>$2.99</price>
       </issue3>
       <issue4>
           <name>Batman Eternal</name>
           <author>Scott Synder</author>
           <publisher>DC Comics</publisher>
           <country>USA</country>
           <year>2012</year>
           <price>$2.99</price>
       </issue4>
       <issue5>
           <name>Batman Eternal</name>
           <author>Scott Synder</author>
           <publisher>DC Comics</publisher>
           <country>USA</country>
           <year>2012</year>
           <price>$2.99</price>
       </issue5>
       <issue6>
           <name>Batman Eternal</name>
           <author>Darren Darcer</author>
           <publisher>DC Comics</publisher>
           <country>USA</country>
           <year>2012</year>
           <price>$2.99</price>
       </issue6>
       <issue7>
           <name>Batman Eternal</name>
           <author>Darren Darcer</author>
           <publisher>DC Comics</publisher>
           <country>USA</country>
           <year>2012</year>
           <price>$2.99</price>
       </issue7>
       <issue8>
           <name>Batman Eternal</name>
           <author>Darren Darcer</author>
           <publisher>DC Comics</publisher>
           <country>USA</country>
           <year>2012</year>
           <price>$2.99</price>
       </issue8>
       <issue9>
           <name>Batman Eternal</name>
           <author>Darren Darcer</author>
           <publisher>DC Comics</publisher>
           <country>USA</country>
           <year>2012</year>
           <price>$2.99</price>
       </issue9>
    </batman>
</comics>

@Tomalak - as you can see I am not getting it...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
<xsl:template match="batman.xsl">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<table border="1">
      <th><td>Name</td><td>Author</td><td>Publisher</td><td>Country</td><td>Year</td><td>Price</td></th>
      <tr bgcolor="#ebebeb">
      <th>name</th>
      <th>author</th>
      <th>publisher</th>
      <th>country</th>
      <th>year</th>
      <th>price</th>
    </tr>
    <xsl:for-each select="comics/batman/issue">
    <tr>
      <td><xsl:value-of select="name"/></td>
      <td><xsl:value-of select="author"/></td>
      <td><xsl:value-of select="publisher"/></td>
      <td><xsl:value-of select="country"/></td>
      <td><xsl:value-of select="year"/></td>
      <td><xsl:value-of select="price"/></td>
    </tr>
    </xsl:for-each>
  </table>
</body>
</html>

</xsl:template>
</xsl:stylesheet>
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129

2 Answers2

1

You were not that far off.

On a general note, XSLT is a programming language, do your indentation properly and consistently.

Also, you're shooting yourself in the foot with element names like <batman> or <issue3>. These elements should be called something like <comic title="Batman"> and <issue number="3">. You should actually change that first in your XML. Never use variable element names.

Here's your attempt, annotated:

<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  version="1.0"
>
  <!-- 
    Error: XHTML is XML, not HTML. Use method="xml", not "html".
  -->
  <xsl:output 
      method="html" 
      encoding="utf-8" 
      doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" 
      doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
  />

  <!-- 
    Error: You can't use "batman.xsl" here.
      You *already are* inside the XML file. Use match expressions
      to refer to specific nodes that the template should be used for.
  -->
  <xsl:template match="batman.xsl">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      </head>
      <body>
        <table border="1">
          <tr bgcolor="#ebebeb">
            <th>name</th>
            <th>author</th>
            <th>publisher</th>
            <th>country</th>
            <th>year</th>
            <th>price</th>
          </tr>
          <!--
            Common mistake: avoid <xsl:for-each>
              XSLT is a template-matching language. You should 
              write templates.
          -->
          <xsl:for-each select="comics/batman/issue">
            <tr>
              <td><xsl:value-of select="name" /></td>
              <td><xsl:value-of select="author" /></td>
              <td><xsl:value-of select="publisher" /></td>
              <td><xsl:value-of select="country" /></td>
              <td><xsl:value-of select="year" /></td>
              <td><xsl:value-of select="price" /></td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

A better approach (needs all your <issueX> elements replaced with <issue>):

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns="http://www.w3.org/1999/xhtml"
>
  <xsl:output 
      method="xml" 
      encoding="utf-8"
      doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" 
      doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
  />

  <!-- create the base output document -->
  <xsl:template match="/comics">
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Comic List</title>
      </head>
      <body>
        <h1>Comics</h1>
        <xsl:apply-templates select="*" />
      </body>
    </html>
  </xsl:template>

  <!-- any children of the "comics" element get a table -->
  <xsl:template match="comics/*">
    <table border="1">
      <caption><xsl:value-of select="name()" /></caption>
      <tr>
        <td>Name</td>
        <td>Author</td>
        <td>Publisher</td>
        <td>Country</td>
        <td>Year</td>
        <td>Price</td>
      </tr>
      <xsl:apply-templates select="issue" />
    </table>
  </xsl:template>

  <!-- any issue becomes a table row -->
  <xsl:template match="issue">
    <tr>
      <td><xsl:apply-templates select="name" /></td>
      <td><xsl:apply-templates select="author" /></td>
      <td><xsl:apply-templates select="publisher" /></td>
      <td><xsl:apply-templates select="country" /></td>
      <td><xsl:apply-templates select="year" /></td>
      <td><xsl:apply-templates select="price" /></td>
    </tr>
  </xsl:template>

  <!-- 
    We don't need to specify a template for <name>, <author>, etc
    because XSLT defaults to "if there's no template defined, copy 
    the text to the output - which is what we want.
    But *if* you want to make the <name> bold, for example,
    all you need to do is create a <xsl:template match="name">.
  -->

</xsl:stylesheet>

To understand <xsl:apply-templates> you might want to read this first: What are the differences between 'call-template' and 'apply-templates' in XSL?

The advantages of breaking up your program into templates are:

  • Smaller templates. You can see what's going on in a template without scrolling.
  • Less nesting makes the code easier to read.
  • Higher re-usability: You can write templates that work for several different inputs and don't have to copy/paste portions of your code.
  • No need for any loops.
  • You get to use the mechanism behind XSLT's default template rules (read about them) and match specificity: You can define default behavior and override it with special behavior without even writing a single if.
Community
  • 1
  • 1
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Thankyou for your help @Tomalak but it is still as clear as mud to me. I just can't grasp the process. I think I will ask my tutor. I thought that my course would teach me html before this stuff but they expect us to know how to write code without a single lesson and it's very hard to understand the theory. Thanks again. – jimbojones5678 Jun 07 '14 at 04:27
  • @jimbojones If you're just beginning to learn about HTML then XSLT is way out out of scope IMHO. – Tomalak Jun 07 '14 at 08:43
0

Your XML text is not valid for the specified DTD!

If specified DTD is wrong and XML text is right then this XSL makes HTML table:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" indent="yes" version="5.0"/>
  <xsl:template match="comics">
    <html>
      <head><link href="styles.css" rel="stylesheet" type="text/css"/></head>
      <body><xsl:apply-templates select="batman"/></body>
    </html>
  </xsl:template>
  <xsl:template match="batman">
    <table>
      <th><td>Name</td><td>Author</td><td>Publisher</td><td>Country</td><td>Year</td><td>Price</td></th>
      <xsl:for-each select="*">
        <tr>
          <td><xsl:value-of select="name"/></td>
          <td><xsl:value-of select="author"/></td>
          <td><xsl:value-of select="publisher"/></td>
          <td><xsl:value-of select="country"/></td>
          <td><xsl:value-of select="year"/></td>
          <td><xsl:value-of select="price"/></td>
        </tr>
      </xsl:for-each>
    </table>
  </xsl:template>
</xsl:stylesheet>

Otherwise, you can to modify this code easily, or me.

ApceH Hypocrite
  • 1,093
  • 11
  • 28
  • I still don't follow. I did the XML first then the DTD, I tried to use the DTD off another example. So if my XML is right what should the intergrated DTD be? And does that make the XSL you did need changing? – jimbojones5678 Jun 06 '14 at 11:23
  • so I only need to use a wildcard for the whole table? – jimbojones5678 Jun 06 '14 at 11:25
  • I forgot to mention it has to have the Element though one author has to have a coloured background and the table needs a border. Whenevber I attempt to add one the whole table messes up? So does the DTD still need altering? What was wrong with it? – jimbojones5678 Jun 06 '14 at 11:45