1

I'm trying to take two separate XML files and with the use of XSL make them into one HTML file. I'm getting the right things from the first XML but when I'm trying the same from the second one, I either get all of the word in a single line or just the first one several times.

XML 1

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="xsltcss.css"?>
<?xml-stylesheet type="text/xsl" href="xslt.xsl" ?>    
<lexicon>
    <head>
        <title>Danish</title>
        <author>Mattias Liljegren</author>
    </head>
    <language value="danish">
        <word value="dog">hund</word>
        <word value="coffee">kaffe</word>
        <word value="tree">træ</word>
        <word value="chair">stol</word>
        <word value="flashlight">lommelygte</word>
        <word value="cat">kat</word>
        <word value="fish">fisk</word>
        <word value="car">bil</word>
        <word value="phone">telefon</word>
        <word value="forest">skov</word>
    </language>
</lexicon>

XML 2

<?xml version="1.0" encoding="UTF-8"?>    
<lexicon>
    <head>
        <title>Croatian</title>
        <author>Mattias Liljegren</author>
    </head>
    <language value="croatian">
        <word value="dog">pas</word>
        <word value="coffee">kava</word>
        <word value="tree">drvo</word>
        <word value="chair">stolica</word>
        <word value="flashlight">baterija</word>
        <word value="cat">mačka</word>
        <word value="fish">riba</word>
        <word value="car">automobil</word>
        <word value="phone">telefon</word>
        <word value="forest">šuma</word>
    </language>
</lexicon>

XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
   <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes" />
   <xsl:template match="/">
      <html>
         <head>
            <link rel="stylesheet" type="text/css" href="xsltcss.css" />
         </head>
         <body>
            <xsl:for-each select="lexicon/head/title">
               <p>
                  <xsl:value-of select="." />
               </p>
               <p>
                  <xsl:value-of select="document('kroatiska.xml')/lexicon/head/title/." />
               </p>
            </xsl:for-each>
            <xsl:for-each select="lexicon/language/word">
               <p>
                  <xsl:value-of select="." />
               </p>
            </xsl:for-each>
            <xsl:for-each select="lexicon/language/word">
               <p>
                  <xsl:value-of select="document('kroatiska.xml')/lexicon/language/word" />
               </p>
            </xsl:for-each>
         </body>
      </html>
   </xsl:template>
</xsl:stylesheet>

Two separate xml files, I want the first word from each, then the second word and so on. Atm I'm getting the right ones from the original xml, but only the first word "pas" from the second xml file.

Expected result:

Danish
hund
kaffe 
trä  
stol  
lommelygte 
kat
fisk
bil
telefon 
skov
Croatian
pas 
kava
drvo
stolica
baterija
macka
riba
automobil
telefon
suma
Parfait
  • 104,375
  • 17
  • 94
  • 125
Burko90
  • 13
  • 3
  • What is the expected result of the transformation? – michael.hor257k Oct 25 '19 at 10:20
  • Also, are you really using an XSLT 2.0 processor? Your stylesheet says `version="2.0"` but this `` suggests you're doing the transformation in a browser, i.e. XSLT 1.0. – michael.hor257k Oct 25 '19 at 10:21
  • Atm I'm getting the top words as separate lines, and the bottom words as either the same amount of lines but only the first word pas, or all the words in a single line several times when i try to edit the code. I want it to take the first word from each xml, then the second word from each xml and so on. I dont know which processor I'm using tbh, doing this for school so I'm abit lost :) – Burko90 Oct 25 '19 at 10:31
  • I am afraid this is confusing. Please edit your question and add the exact result you expect to get from the given example. -- If you don't know which processor you're using, find out: https://stackoverflow.com/questions/25244370/how-can-i-check-which-xslt-processor-is-being-used-in-solr/25245033?r=SearchResults&s=1|27.1600#25245033 – michael.hor257k Oct 25 '19 at 10:37
  • I've updated the bottom now so I hope its less confusing. I'm using version 1! edited it from 2.0 to 1, thanks! – Burko90 Oct 25 '19 at 10:42
  • I still don't see the expected result. – michael.hor257k Oct 25 '19 at 10:44
  • Updated again with window – Burko90 Oct 25 '19 at 10:53

3 Answers3

1

I thought you would want to put the two lists side-by-side, as in a dictionary. To get the shown result of two separate lists is rather trivial:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>

<xsl:template match="/lexicon">
    <html>
        <body>
            <!-- this document -->
            <h3>
                <xsl:value-of select="language/@value"/>
            </h3>
            <xsl:for-each select="language/word">
                <p>
                    <xsl:value-of select="."/>
                </p>
            </xsl:for-each>
            <!-- external document -->
            <xsl:variable name="kroatiska" select="document('kroatiska.xml')/lexicon" />
            <h3>
                <xsl:value-of select="$kroatiska/language/@value"/>
            </h3>
            <xsl:for-each select="$kroatiska//language/word">
                <p>
                    <xsl:value-of select="."/>
                </p>
            </xsl:for-each>
        </body>
    </html>
</xsl:template>

</xsl:stylesheet>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
0

Per the engaging XSLT discussion For loops vs. apply-templates, consider an alternative version to avoid the the many <xsl:for-each> calls. As advised by XSLT gurus (@Tomalak, @DimitreNovatchev, @MichaelKay):

  • Using for-each makes your program more complex by adding nesting levels and it's also impossibe to re-use the code inside the for-each block. Using apply-templates will (when done right) generates more flexible and modular XSLT.

  • Using <xsl:template> and <xsl:apply-templates> is much more powerful and elegant...xsl:apply-templates is much richer and deeper than xsl:for-each, even simply because we don't know what code will be applied on the nodes of the selection -- in the general case this code will be different for different nodes of the node-list.

  • The main benefits of xsl:apply-templates over for-each are that the code adapts better to changing document structures and changing processing requirements.


In your case, since you need to traverse the nodes in external document, xsl:for-each, is unavoidable:

XSLT (using lexicon, language, and word templates)

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

   <xsl:template match="/lexicon">
        <html>
            <head>
                <link rel="stylesheet" type="text/css" href="xsltcss.css" />
            </head>
            <xsl:apply-templates select="language" />
        </html>
   </xsl:template>   

   <xsl:template match="language">
        <body>
            <xsl:variable name="other_doc" select="document('kroatiska.xml')" />
            <h3>
               <xsl:value-of select="@value"/>
            </h3>
            <xsl:apply-templates select="word" />
            <h3>
                <xsl:value-of select="$other_doc/lexicon/language/@value" />
            </h3>
            <xsl:for-each select="$other_doc/lexicon/language/word">
                <p>
                  <xsl:value-of select="text()" />
                </p>
            </xsl:for-each>
        </body>
   </xsl:template>   

   <xsl:template match="word">
        <p>
          <xsl:value-of select="text()" />
        </p>
   </xsl:template>   

</xsl:stylesheet>

HTML

<html>
  <head>
    <META http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link rel="stylesheet" type="text/css" href="xsltcss.css">
  </head>
  <body>
    <h3>danish</h3>
    <p>hund</p>
    <p>kaffe</p>
    <p>træ</p>
    <p>stol</p>
    <p>lommelygte</p>
    <p>kat</p>
    <p>fisk</p>
    <p>bil</p>
    <p>telefon</p>
    <p>skov</p>
    <h3>croatian</h3>
    <p>pas</p>
    <p>kava</p>
    <p>drvo</p>
    <p>stolica</p>
    <p>baterija</p>
    <p>mačka</p>
    <p>riba</p>
    <p>automobil</p>
    <p>telefon</p>
    <p>šuma</p>
  </body>
</html>
Parfait
  • 104,375
  • 17
  • 94
  • 125
  • Why do you think that *"since you need to traverse the nodes in external document, `xsl:for-each`, is unavoidable"*? -- P.S. I believe using `xsl:for-each` is a better choice in this case: it makes the code shorter and more readable. Your version, IMHO, suffers from the GOTO syndrome. – michael.hor257k Oct 25 '19 at 15:15
  • If you can show how to avoid the `xsl:for-each` on nodes via `document()`, I am very interested! Maybe in 2.0? Also, OP may have a much larger XML with other nodes and only posted a snippet here. Plus, future, novice XSLT readers may believe multiple `for-each` from root is the only way. Again, this is an alternative approach, hopefully citing a general best practice. – Parfait Oct 25 '19 at 17:58
  • I have posted another answer showing that. -- There is no "general best practice". Each case needs to be weighed on its own. – michael.hor257k Oct 25 '19 at 21:21
0

This is an expansion of the comments following the answer by Parfait.

In his answer, Parfait says:

In your case, since you need to traverse the nodes in external document, xsl:for-each, is unavoidable:

In a comment I asked why would that be, and Parfait replied:

If you can show how to avoid the xsl:for-each on nodes via document(), I am very interested!

The following stylesheet shows how to get the same result using only apply-templates:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>

<xsl:template match="/lexicon">
    <html>
        <body>
            <xsl:apply-templates select="language | document('kroatiska.xml')/lexicon/language" />
        </body>
    </html>
</xsl:template>

<xsl:template match="language">
    <h3>
        <xsl:value-of select="@value"/>
    </h3>
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="word">
    <p>
        <xsl:value-of select="."/>
    </p>
</xsl:template>

</xsl:stylesheet>

This is not meant to imply that using xsl:apply-templates is somehow better in this case than xsl:for-each.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51