Tutorial :accessing xml attributes in xsl



Question:

i've this xml code:

<title xml:lang="ar">أربيك</title>  <title xml:lang="en">English</title>  

which i'm formatting in xsl:

<div class="title">          <xsl:value-of select="root/title"/>  </div>  

however, this^ only displays the arabic title, and not the english one. i tried this code:

<div class="title">          <xsl:attribute name="xml:lang"><xsl:value-of select="root/title"/> </xsl:attribute>  </div>  

but with this^ code, it doesn't display the titles at all. what is the correct way to display both the english and the arabic titles?


Solution:1

The following will work.

Source XML:

$xmlDoc = <<< XML  <titles>      <title xml:lang="ar">أربيك</title>      <title xml:lang="en">English</title>  </titles>  XML;  

XSL Stylesheet with template matching any title nodes in the document

$xslDoc = <<< XSL  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">        <xsl:template match="/">             <h1>Titles</h1>          <xsl:apply-templates/>      </xsl:template>        <xsl:template match="title">          <div class="{@xml:lang} title">              <xsl:value-of select="."/>          </div>      </xsl:template>    </xsl:stylesheet>  XSL;  

And transformation with PHP:

$xml = new DOMDocument();  $xml->loadXML($xmlDoc);  $xsl = new DOMDocument;  $xsl->loadXML($xslDoc);  $proc = new XSLTProcessor;  $proc->importStyleSheet($xsl);  echo $proc->transformToXML($xml);  

will give:

<?xml version="1.0"?>  <h1>Titles</h1>      <div class="ar title">أربيك</div>      <div class="en title">English</div>  

EDIT: modified title template to use the xml:lang attribute as class attribute, so you can style it with CSS. If you need more complex styling, write another template that matches the attribute (as shown by Volker).


Solution:2

You could try for-each:

<xsl:for-each select="root/title">      <xsl:value-of select="."/>  </xsl:for-each>  

I am not 100% sure but I think value-of will only get the value of one element, and if several are selected, it just gets the value of the first one.


Solution:3

A title template will be the best way to do this:

<xsl:template match="title">    <div class="title">      <xsl:value-of select="." />    </div>  </xsl:template>  

You could use xsl:for-each to achieve the same, but a template is the idiomatic xsl.


Solution:4

You can have more than one template that matches a specific node. In that case the matching templates are prioritized and only the template with the highest priority is used. If you don't specifically set a priority there are built-in rules to determine the priority, see 5.5 Conflict Resolution for Template Rules.

E.g. if you have the two templates

  1. <xsl:template match="title">...</xsl:template>
  2. <xsl:template match="title[@xml:lang='ar']">...</xsl:template>

they both match <title xml:lang="ar">أربيك</title>.
But the one testing the xml:lang attribute (template #2) is "more specific than the most common kind of pattern" and gets a higher priority than the template that simply matches all <title> elements (template #1). Therefore template #2 is selected.
For <title xml:lang="en">title 2</title> there is only one matching template (template #1) which is therefore selected.

self-contained example:

<?php  $proc = <<< XSL  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">    <xsl:template match="/">      <xsl:apply-templates/>    </xsl:template>      <xsl:template match="title">      <h2>template #1: <xsl:value-of select="."/></h2>    </xsl:template>      <xsl:template match="title[@xml:lang='ar']">      <h1>template #2: <xsl:value-of select="."/></h1>    </xsl:template>    </xsl:stylesheet>  XSL;  $proc = getProcessor($proc);    $doc = <<< XML  <titles>      <title xml:lang="ar">أربيك</title>      <title xml:lang="en">english title</title>  </titles>  XML;  $doc = getDocument($doc);    echo $proc->transformToXML($doc);      function getDocument($xml) {    $doc = new DOMDocument;    $doc->loadxml($xml);    return $doc;  }    function getProcessor($xml) {    $proc = new XSLTProcessor;    $proc->importStylesheet(getDocument($xml));    return $proc;  }  

prints

<?xml version="1.0"?>        <h1>template #2: أربيك</h1>      <h2>template #1: english title</h2>  


Solution:5

Probably the shortest, complete solution, that is also completely in the spirit of XSLT is the following:

<xsl:stylesheet version="1.0"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:output omit-xml-declaration="yes" indent="yes"/>   <xsl:strip-space elements="*"/>     <xsl:template match="title">      <div class="title">              <xsl:value-of select="."/>      </div>   </xsl:template>  </xsl:stylesheet>  

when this transformation is applied on the following XML document (just wrapping the provided XML fragment into a single top element):

<titles>      <title xml:lang="ar">أربيك</title>      <title xml:lang="en">English</title>  </titles>  

the wanted result is produced:

<div class="title">أربيك</div>  <div class="title">English</div>  


Solution:6

I think that your first attempt was in the right direction: just copy the @xml:lang and apply CSS style later. So this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <xsl:template match="titles">          <html>              <xsl:apply-templates/>          </html>      </xsl:template>      <xsl:template match="title">          <div class="title">              <xsl:copy-of select="@xml:lang"/>              <xsl:value-of select="."/>          </div>      </xsl:template>  </xsl:stylesheet>  

With proper input (from Dimitre):

<titles>      <title xml:lang="ar">أربيك</title>      <title xml:lang="en">English</title>  </titles>  

Output:

<html>  <div class="title" xml:lang="ar">أربيك</div>  <div class="title" xml:lang="en">English</div>  </html>  

Or this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <xsl:template match="titles">          <html>              <xsl:apply-templates/>          </html>      </xsl:template>      <xsl:template match="title">          <div class="title" lang="{@xml:lang}">              <xsl:value-of select="."/>          </div>      </xsl:template>  </xsl:stylesheet>  

Output:

<html>  <div class="title" lang="ar">أربيك</div>  <div class="title" lang="en">English</div>  </html>  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »