Is there a way to determine if a a root level node contains any child nodes?
I have this code file that builds up a navigation menu for a drop-down menu, but for the root nodes that have no nodes below them I want to apply a different template to them:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/Home">
<xsl:apply-templates select="root" />
</xsl:template>
<xsl:template match="root">
<ul class="nav navbar-nav">
<xsl:apply-templates select="node">
<xsl:with-param name="level" select="0"/>
</xsl:apply-templates>
</ul>
</xsl:template>
<xsl:template match="node">
<xsl:param name="level" />
<xsl:choose>
<xsl:when test="$level=0">
<li>
<xsl:attribute name="class">
<xsl:if test="#breadcrumb = 1">active</xsl:if>
<xsl:if test="node">
<xsl:text> dropdown</xsl:text>
</xsl:if>
</xsl:attribute>
<xsl:choose>
<xsl:when test="#enabled = 1">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<xsl:attribute name="class">
<xsl:if test="node">
<xsl:text>dropdown-toggle</xsl:text>
</xsl:if>
</xsl:attribute>
<xsl:if test="node">
<xsl:attribute name="data-toggle">dropdown</xsl:attribute>
</xsl:if>
<xsl:value-of select="#text" />
<xsl:if test="node">
<b class="caret"></b>
</xsl:if>
</a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#text" />
</xsl:otherwise>
</xsl:choose>
<xsl:if test="node">
<ul class="dropdown-menu">
<xsl:apply-templates select="node">
<xsl:with-param name="level" select="$level + 1" />
</xsl:apply-templates>
</ul>
</xsl:if>
</li>
</xsl:when>
<xsl:otherwise>
<li>
<xsl:attribute name="class">
<xsl:if test="#breadcrumb = 1">active</xsl:if>
<xsl:if test="node">
<xsl:text> dropdown</xsl:text>
</xsl:if>
</xsl:attribute>
<xsl:choose>
<xsl:when test="#enabled = 1">
<a href="{#url}">
<xsl:value-of select="#text" />
</a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#text" />
</xsl:otherwise>
</xsl:choose>
</li>
<xsl:if test="node">
<!-- no extra level in default bootstrap -->
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Your question is not clear. If the root node does not have any child nodes, then the XML document is empty. Perhaps you meant the root element; there will be exactly one element like that and it's easy to see if it has any child nodes by using:
test="/*/node()"
in an xsl:if or xsl:when instruction.
Alternatively, you could use two templates - one matching a root element with child nodes:
<xsl:template match="/*[node()]">
and one for the other case:
<xsl:template match="/*[not(node())]">
You can use this XSLT-file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match="/*[count(descendant::*) = 0]">
No siblings
</xsl:template>
<xsl:template match="/*[count(descendant::*) > 0]">
Has siblings
</xsl:template>
</xsl:stylesheet>
With an input XML file with one or more siblings of the root element like this
<?xml version="1.0"?>
<root>
<a />
</root>
it will output "Has siblings".
And with an input file with an empty root tag like this
<?xml version="1.0"?>
<root>
</root>
it will output "No siblings".
If I understand the question correctly, try adding the template rule
<xsl:template match="root[not(*)]"/>
Related
I want to show the property of tag Page, like this xml:
<format class="Book">BookTitle </format>Test <page number="51" />….... </p>
I have this xslt for convert xml to html
<xsl:template match="format" name="format">
<xsl:choose>
<xsl:when test="name() = 'format'" >
<xsl:if test ="#class = 'q'">
<xsl:call-template name="q" />
</xsl:if>
<xsl:if test ="#class = 'Book'">
<xsl:call-template name="Book" />
</xsl:if>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Book" name=" Book">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="page" name="page">
<xsl:value-of select="#number" />
</xsl:template>
But, attribute of tag Page is not displayed, because this tag is as child of tag Book
What should I do?
<xsl:template match="Book" name=" Book">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="page" name="page">
<xsl:value-of select="#number" />
</xsl:template>
Change to
<xsl:template match="Book" name=" Book">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="page" name="page">
<xsl:value-of select=" #number"/>
</xsl:template>
I need to ask if Range of Supplier_ID between 985000 and 989999 then set in tag AllocationNumber the value 'FX' else let it empty.
I thought to solve it with the starts with xsl function.
But it does not work (I'm new in XSL)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:user="http://www.altova.com/MapForce/UDF/user" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="user xs fn user">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Form">
<MaxPostInvoice>
<xsl:attribute name="xsi:noNamespaceSchemaLocation">C:\Program Files (x86)\MaxPostInvoice-v1.18.xsd</xsl:attribute>
<Header>
<VendorID>
<xsl:for-each select="Fields">
<xsl:for-each select="Supplier_ID">
<xsl:for-each select="#source">
<xsl:attribute name="source"><xsl:value-of select="."/></xsl:attribute>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="Fields">
<xsl:for-each select="Supplier_ID">
<xsl:for-each select="#confidence">
<xsl:attribute name="confidence"><xsl:value-of select="."/></xsl:attribute>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="Fields">
<xsl:for-each select="Supplier_ID">
<xsl:for-each select="#rawContents">
<xsl:attribute name="rawValue"><xsl:if test="//Fields/Supplier_ID[#rawContents = '']"><xsl:value-of select="''"/></xsl:if><xsl:if test="//Fields/Supplier_ID[#rawContents != 'X']"><xsl:value-of select="."/></xsl:if></xsl:attribute>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="Fields">
<xsl:for-each select="Supplier_ID">
<xsl:for-each select="#contents">
<xsl:attribute name="value"><xsl:if test="//Fields/Supplier_ID[#contents = 'X']"><xsl:value-of select="''"/></xsl:if><xsl:if test="//Fields/Supplier_ID[#contents != 'X']"><xsl:value-of select="."/></xsl:if></xsl:attribute>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</VendorID>
<AllocationNumber>
<xsl:attribute name="rawValue">
<xsl:if test="number(./Fields/Supplier_ID/#contents) >= 985000 and number(./Fields/Supplier_ID/#contents) <= 989999">
<xsl:value-of select="FX"/>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:if test="number(./Fields/Supplier_ID/#contents) >= 985000 and number(./Fields/Supplier_ID/#contents) <= 989999">
<xsl:value-of select="FX"/>
</xsl:if>
</xsl:attribute>
</AllocationNumber>
</Header>
</MaxPostInvoice>
</xsl:template>
</xsl:stylesheet>
This is the input XML:
<Form name="FP0001">
<Fields>
<Supplier_ID contents="985000" rawContents="" source="OCR" confidence="100"/>
<Supplier_Name contents="Leifheitstr" rawContents="" source="OCR" confidence="100"/>
<Supplier_Street contents="abcde" rawContents="" source="OCR" confidence="100"/>
</Fields>
</Form>
Result
<Header>
<VendorID source="OCR" confidence="100" rawValue="" value="985000 " />
<AllocationNumber rawValue="" />
</Header>
You have not provided a input XML, if your XPath is correct, you could do:
<xsl:attribute name="rawValue">
<xsl:if test="number(./Fields/Supplier_ID/#contents) >= 985000 and number(./Fields/Supplier_ID/#contents) <= 989999">
<xsl:value-of select="FX"/>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="rawValue">
<xsl:if test="number(./Fields/Supplier_ID/#contents) >= 985000 and number(./Fields/Supplier_ID/#contents) <= 989999">
<xsl:value-of select="FX"/>
</xsl:if>
</xsl:attribute>
If it not works share your input XML and more of your XSLT (complete).
Why do you have the same attribute twice? In any case, you need to change:
<xsl:value-of select="FX"/>
to:
<xsl:value-of select="'FX'"/>
BTW, it seems to me you could simplify this thing significantly to something like:
<xsl:template match="/Form/Fields">
<MaxPostInvoice xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="C:\Program Files (x86)\MaxPostInvoice-v1.18.xsd">
<Header>
<VendorID source="{Supplier_ID/#source}" confidence="{Supplier_ID/#confidence}" >
<xsl:attribute name="rawValue">
<xsl:if test="Supplier_ID/#rawContents != 'X'">
<xsl:value-of select="Supplier_ID/#rawContents"/>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:if test="Supplier_ID/#contents != 'X'">
<xsl:value-of select="Supplier_ID/#contents"/>
</xsl:if>
</xsl:attribute>
</VendorID>
<AllocationNumber>
<xsl:attribute name="rawValue">
<xsl:if test="number(Supplier_ID/#contents) >= 985000 and number(Supplier_ID/#contents) <= 989999">
<xsl:value-of select="'FX'"/>
</xsl:if>
</xsl:attribute>
</AllocationNumber>
</Header>
</MaxPostInvoice>
</xsl:template>
I have the following bit of code to transform one xml document into another via xslt
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(new javax.xml.transform.stream.StreamSource(
this.getClass().getClassLoader().getResourceAsStream(ie.getXslFileName())));
transformer.transform(
new javax.xml.transform.stream.StreamSource(reader),
new javax.xml.transform.stream.StreamResult(stream));
output.put(fileName, stream.toByteArray());
It works perfectly if I do not attempt to select disctinct values. However, as soon as I put in the generate-id line and key it fails. In every other test tool I use the transformation is perfect so I am thinking it is a limitation in jaxb's transformer? Does anyone have any suggestion as to how I can select unique values that will work here AND enable jaxb transformation? OR, alternatively, is there another transformer I could use that would do the trick?
XSLT that works everywhere BUT here:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
<xsl:key name="groupKey"
match="//questionGroup/externalCodes/externalCode/externalCode/text()"
use="." />
<xsl:template match="/">
<xsl:apply-templates select="submissionReport"/>
</xsl:template>
<xsl:template match="submissionReport" >
<component>
<structuredBody>
<xsl:for-each select="//questionResponse/question/questionGroup
/externalCodes/externalCode[governingBody
[internalCode='GVB-AHRQ-1']]
/externalCode/text()[generate-id() =
generate-id(key('groupKey',.)[1])]">
<component>
<section>
<entry>
<templateId>
<xsl:attribute name="root">
<xsl:value-of select="."/>
</xsl:attribute>
</templateId>
<organizer classCode="CLUSTER" moodCode="EVN">
<id nullFlavor="NA"/>
<statusCode code="completed"/>
<xsl:call-template name="groupedResponses">
<xsl:with-param name="groupInternalCode" select="."/>
</xsl:call-template>
</organizer>
</entry>
</section>
</component>
</xsl:for-each>
</structuredBody>
</component>
</xsl:template>
<xsl:template name="groupedResponses">
<xsl:param name="groupInternalCode"/>
<xsl:for-each select="//questionResponse[question[externalCodes
[externalCode[governingBody[internalCode='GVB-AHRQ-1'] and
not(externalCode='DE42') and not(externaCode='DE3') and
not(externalCode='DE46') and not(externalCode='DE49') and
not(externalCode='DE30')]] and
questionGroup[externalCodes[externalCode[governingBody[internalCode='GVB-AHRQ-1'] and
externalCode=$groupInternalCode]]]]]">
<component>
<observation>
<xsl:attribute name="classCode">OBS</xsl:attribute>
<xsl:attribute name="moodCode">EVN</xsl:attribute>
<templateId>
<xsl:attribute name="root">2.16.840.1.113883.3.263.1.11.3.100</xsl:attribute>
</templateId>
<xsl:for-each select="question/externalCodes/externalCode
[governingBody[internalCode='GVB-AHRQ-1'] or
governingBody[internalCode='GVB-CDCRACE-1'] or
governingBody[internalCode='GVB-HL7NULL-1'] or
governingBody[internalCode='GVB-HL7GENDER-1']]">
<!-- Question information -->
<code>
<xsl:attribute name="code">
<xsl:value-of select="externalCode"/>
</xsl:attribute>
<xsl:attribute name="displayName">
<xsl:value-of select="../../text"/>
</xsl:attribute>
<xsl:attribute name="codeSystem">
<xsl:value-of select="governingBody/externalCode"/>
</xsl:attribute>
<xsl:attribute name="codeSystemName">
<xsl:value-of select="governingBody/name"/>
</xsl:attribute>
</code>
<!-- Response information. Can be more than one -->
<xsl:for-each select="../../../response">
<value>
<xsl:choose>
<xsl:when test="responseOption">
<xsl:for-each select="responseOption/externalCodes/externalCode">
<value>
<xsl:attribute name="type">CD</xsl:attribute>
<xsl:attribute name="code">
<xsl:value-of select="externalCode"/>
</xsl:attribute>
<xsl:attribute name="displayName">
<xsl:value-of select="../../response"/>
</xsl:attribute>
<xsl:attribute name="codeSystem">
<xsl:value-of select="governingBody/externalCode"/>
</xsl:attribute>
<xsl:attribute name="codeSystemName">
<xsl:value-of select="governingBody/name"/>
</xsl:attribute>
</value>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<value>
<xsl:attribute name="type">ED</xsl:attribute>
<xsl:attribute name="mediaType">text/plain</xsl:attribute>
<xsl:value-of select="response"/>
</value>
</xsl:otherwise>
</xsl:choose>
</value>
</xsl:for-each>
</xsl:for-each>
</observation>
</component>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I decided to go with importing Saxon to enable XSLT 2.0. Seconds later grouping is perfect and works easily.
Let's say we have following data:
<all>
<item id="1"/>
<item id="2"/>
...
<item id="N"/>
</all>
What is the most elegant, xslt-ish way to group those items?
For example, imagine we want a table with two cells in each row.
Off the top of my head I can imagine (not tested though)
in template, matching item, I can call this very item, selecting following-sibling.
But even in this case I should pass additional param, to make recursion finite.
As row-count can be variable .. am passing it as a param to the template .. :)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/all[node]">
<table>
<xsl:for-each select="node[1]">
<xsl:call-template name="whoaa">
<xsl:with-param name="count" select="'1'"/>
<xsl:with-param name="row_count" select="'10'"/>
<!--maximum row_count is set to 10 -->
</xsl:call-template>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="whoaa">
<xsl:param name="count"/>
<xsl:param name="row_count"/>
<!--check if we have crossed row_count-->
<xsl:if test="not ($row_count < $count)">
<tr>
<td>
<xsl:value-of select="."/>
</td>
<td>
<!--copy next column-->
<xsl:for-each select="following-sibling::node[1]">
<xsl:value-of select="."/>
</xsl:for-each>
</td>
</tr>
<!--Select next row .. call the same template untill we reach (row_count > count)-->
<xsl:for-each select="following-sibling::node[2]">
<xsl:call-template name="whoaa">
<xsl:with-param name="count" select="$count+2"/>
<xsl:with-param name="row_count" select="$row_count"/>
</xsl:call-template>
</xsl:for-each>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
use position and mod, e.g.
<xsl:template match="/all">
<table>
<xsl:apply-templates name="item" mode="group"/>
</table>
</xsl:template>
<xsl:template match="item[position() mod 2=1]" mode="group">
<tr>
<td><xsl:apply-templates select="." mode="render"/></td>
<td><xsl:apply-templates select="following-sibling::item[1]" mode="render"/></td>
</tr>
</xsl:template>
<xsl:template match="item[position() mod 2=0]"></xsl:template>
<xsl:template match="item" mode="render">item: <xsl:value-of select="#id"/></xsl:template>
I have an xsl template that I want to call to put into an HTML node attribute:
<xsl:variable name="onClickJavaScript">
<xsl:call-template name="GetOnClickJavaScript" />
</xsl:variable>
<a id="123">
<xsl:attribute name="onclick">
<xsl:value-of select="$onClickJavaScript" />
</xsl:attribute>
</a>
My problem is that the call-template returns a node set and the attribute value wants to be a string. I can't seem to find any way to transform the node set into a string that works.
I have tried changing the value-of line to:
<xsl:value-of select="$onClickJavaScript/text()" />
But it complains that it needs to return a node set and that does not (which is what I was hoping, without the complaining part).
In the Visual Studio debugger I can see that $onClickJavaScript is a NodeSet with a single dimension and item. My string is buried in there, but not accessible :-(.
UPDATE...
When I call my template, it goes through a big case statement and calls another template. This other template has a <xsl:choose> and it goes down the following path in this case:
<xsl:when test="$isPrimary='true'">
<!-- Just provide the onclick event JavaScript -->
<xsl:value-of select='concat($launchMethod, "(this, '", $id, "', '", $nodeid, "', '", $instanceid, "')")' />
</xsl:when>
With the information provided, your first sample should work, i.e.
<xsl:attribute name="onclick">
<xsl:value-of select="$onClickJavaScript" />
</xsl:attribute>
should insert an onclick attribute with the (string) value of $onClickJavaScript.
You can even simplify your XSLT by using an attribute value template:
<a id="123" onclick="{$onClickJavaScript}">...</a>
Or get rid of the variable and call the template inside the attribute node:
<xsl:attribute name="onclick">
<xsl:call-template name="GetOnClickJavaScript" />
</xsl:attribute>
My problem is that the call-template
returns a node set and the attribute
value wants to be a string. I can't
seem to find any way to transform the
node set into a string that works.
The problem is in the code you haven't shown.
In fact, I cannot reproduce this problem at all.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="onClickJavaScript">
<xsl:call-template name="GetOnClickJavaScript" />
</xsl:variable>
<xsl:variable name="onClickJavaScript2">
<xsl:call-template name="GetOnClickJavaScript2" />
</xsl:variable>
<a id="123">
<xsl:attribute name="onclick">
<xsl:value-of select="$onClickJavaScript" />
</xsl:attribute>
</a>
<a id="123456">
<xsl:attribute name="onclick">
<xsl:value-of select="$onClickJavaScript" />
</xsl:attribute>
</a>
</xsl:template>
<xsl:template name="GetOnClickJavaScript">Hello</xsl:template>
<xsl:template name="GetOnClickJavaScript2">
<a>Hello</a>
</xsl:template>
</xsl:stylesheet>
when applied on any XML document (not used) produces its output without any errors:
<a id="123" onclick="Hello"/>
<a id="123456" onclick="Hello"/>
I'm not able to reproduce this either... here's what I tried, the onclick on the a gets "foo(this, '3', 'test')". What else is different? What is the result-tree-fragment that you're trying to call xsl:value-of on?
<xsl:template match="/">
<xsl:variable name="onclick-val">
<xsl:call-template name="foo" />
</xsl:variable>
<a>
<xsl:attribute name="onclick">
<xsl:value-of select="$onclick-val" />
</xsl:attribute>
</a>
</xsl:template>
<xsl:template name="foo">
<xsl:choose>
<xsl:when test="false()">
false
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="callme" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="callme">
<xsl:choose>
<xsl:when test="true()">
<xsl:value-of select='concat("foo", "(this, '", "3", "', '", "test", "')")' />
</xsl:when>
<xsl:otherwise>
false
</xsl:otherwise>
</xsl:choose>
</xsl:template>