accessing xml attributes in xsl
i've this xml code:开发者_Python百科
<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?
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).
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.
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.
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
- <xsl:template match="title">...</xsl:template>
- <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>
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>
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>
精彩评论