I am trying to change some text from XML. However it did not work.
I have to display all of [intro] text from XML
some Path(text) should change before display [intro].
For example
<a href="3DD3D025-2236-49C9-A169-DD89A36DA0E6/eee.pdf"> --> wrong path
I would like to change to
<a href="Content\3\D\D\3DD3D025-2236-49C9-A169-DD89A36DA0E6/eee.pdf">
Any help would be greatly appreciated.
Sample XML
<?xml version="1.0"?>
<root>
<intro xml:lang="en">
<div class="blueBar">
<h2>Highlights</h2>
<ul>
<li>aaaa</li>
<li>bbbb</li>
<li>ccc</li>
<li>pdf</li>
</ul>
</div>
</intro>
<intro> .....</intro>
</root>
Sample XSLT
<xsl:param name="language"/>
<xsl:template match="root">
<xsl:for-each select="intro[lang($language)]//#href">
<xsl:choose>
<xsl:when test="contains(.,'pdf') and not(contains(.,'Content'))">
<xsl:variable name="pdfGuid">
<xsl:value-of select="substring(.,0,36)"/>
</xsl:variable>
<xsl:variable name="pdfPath">
<xsl:value-of select="concat('/','Content')"/>
<xsl:value-of select="concat('/', substring($pdfGuid, 1,1))"/>
<xsl:value-of select="concat('/', substring($pdfGuid, 2,1))"/>
<xsl:value-of select="concat('/', substring($pdfGuid, 3,1))"/>
<xsl:value-of select="concat('/', $pdfGuid)"/>
</xsl:variable>
<xsl:value-of select="strJS:replace(string(.),string($pdfGuid),string($pdfPath))" disable-output-escaping="yes"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<div class="greenBox">
<xsl:value-of select="intro[lang($language)]" disable-output-escaping="yes"/>
</div>
</xsl:template>
When you ask a question here, please clearly explain the output you want and what the problem is that you are encountering.
There is a discrepancy between your description of the problem and your attempt, wherein you are using forward slashes, and one at the beginning of the path in your XSLT, and backslashes in your description. I am going to assume you want forward slashes, but these can easily be changed. I suspect what you want is something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="language" select="'en'" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<div class="greenBox">
<xsl:apply-templates select="intro[lang($language)]/node()" />
</div>
</xsl:template>
<xsl:template match="#href[contains(., 'pdf') and not(contains(., 'Content'))]">
<xsl:attribute name="href">
<xsl:value-of select="concat('/Content/',
substring(., 1, 1), '/',
substring(., 2, 1), '/',
substring(., 3, 1), '/',
.)"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, the result is:
<div class="greenBox">
<div class="blueBar">
<h2>Highlights</h2>
<ul>
<li>
aaaa
</li>
<li>
bbbb
</li>
<li>
ccc
</li>
<li>
pdf
</li>
</ul>
</div>
</div>
Related
I need to format some xml data with the following structure
<list>
<item>
Test
</item>
<item>
Testt
</item>
<or-item>
TestOr
</or-item>
<or-item>
TestOrr
</or-item>
<item>
Testtt
</item>
<or-item>
TestOrrr
</or-item>
<item>
Testttt
</item>
</list>
with xsl:number the or-item must be formatted with the second level count on that position. I know it would be better to structure the or-item inside that item but the data is given like that.
I need a way to count the or-item next to the current or-item to calculate the numbering for xsl:number
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.1"
xmlns:axf="http://www.antennahouse.com/names/XSL/Extensions" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output encoding="UTF-8" method="html" indent="yes"/>
<xsl:template match="list">
<div>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="item">
<div>
<xsl:number count="item"/>
<xsl:value-of select="."/>
</div>
</xsl:template>
<xsl:template match="or-item">
<div style="padding-left: 10px">
<xsl:number value="count(//or-item)" format="a) "/>
<xsl:value-of select="."/>
</div>
</xsl:template>
</xsl:stylesheet>
Edit
I am using XSLT 1.1 with xsltproc on linux but 2.0 whould be possible if neccessary
As the target format is HTML, it seems you could rely on creating the appropriate nested HTML ordered lists by using xsl:for-each-group and group-starting-with="item":
<xsl:template match="list">
<ol>
<xsl:for-each-group select="*" group-starting-with="item">
<li>
<xsl:value-of select="."/>
<xsl:where-populated>
<ol>
<xsl:apply-templates select="tail(current-group())"/>
</ol>
</xsl:where-populated>
</li>
</xsl:for-each-group>
</ol>
</xsl:template>
<xsl:template match="or-item">
<li>
<xsl:value-of select="."/>
</li>
</xsl:template>
https://xsltfiddle.liberty-development.net/ejivJrM
That example uses some XSLT/XPath 3 stuff like were-populated and tail but in case that XSLT 2 compatility is needed then it could be replaced by <xsl:if test="subsequence(current-group(), 2)"><ol><xsl:apply-templates select="subsequence(current-group(), 2)"/></xsl:if>.
And of course the use of HTML ordered lists is not necessary, if needed/wanted you could just transform the input to nested divs with the used grouping approach and then in a second step use format-number as you seem to want to do:
<xsl:template match="list">
<xsl:variable name="nested-list">
<xsl:for-each-group select="*" group-starting-with="item">
<xsl:copy>
<xsl:value-of select="."/>
<xsl:copy-of select="tail(current-group())"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:variable>
<div>
<xsl:apply-templates select="$nested-list"/>
</div>
</xsl:template>
<xsl:template match="item">
<div>
<xsl:number/>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="or-item">
<div style="padding-left: 10px">
<xsl:number format="a) "/>
<xsl:value-of select="."/>
</div>
</xsl:template>
https://xsltfiddle.liberty-development.net/ejivJrM/1
You can produce the expected output by simply adjusting the xsl:number instruction:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8"/>
<xsl:template match="/list">
<div>
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="item">
<div>
<xsl:number/>
<xsl:value-of select="."/>
</div>
</xsl:template>
<xsl:template match="or-item">
<div style="padding-left: 10px">
<xsl:number level="any" from="item" format="a) "/>
<xsl:value-of select="."/>
</div>
</xsl:template>
</xsl:stylesheet>
How can I get only "Your friend" or only "Mickey Mouse" from these nodes?
<span>
<lb/>
Your Friend
<lb/>
<name> Mickey Mouse </name>
</span>
My desired output from XSL would be this:
<p> Your Friend </p>
<p> Mickey Mouse </p>
I have tried with:
<xsl:template match="/">
<xsl:for-each select="./lb">
<p>
<xsl:if test="./following-sibling::text()[1]">
<xsl:value-of select="./following-sibling::*[text()][1]"/>
</xsl:if>
<xsl:if test="./following-sibling::*[name]//text()">
-test-
</xsl:if>
</p>
</xsl:for-each>
</xsl:template>
but I know I'm completely wrong since I never get to -test-
I am mostly guessing here, but it seems you want:
<xsl:template match="span">
<xsl:for-each select="lb">
<p>
<xsl:value-of select="(following-sibling::text()|following-sibling::name)[1]"/>
</p>
</xsl:for-each>
</xsl:template>
use code:
<xsl:strip-space elements="*"/>
<xsl:template match="text()">
<p>
<xsl:value-of select="normalize-space(.)"/>
</p>
</xsl:template>
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(*)]"/>
I'm trying to transform the following XML..
<?xml version="1.0" encoding="utf-16"?><Member TextRank="unknown" FullName="My Name" ..etc.. />
Into something like the following,
<div class="member">
<span class="label">
Text Rank (note: i want to override the labels in the xslt)
</div>
<span class="value">
unknown
</span>
<span class="label">
Full Name
</div>
<span class="value">
My Name
</span>
..etc..
</div>
How if possible could I do this using xslt?
Here's a different approach, that does away for the need for the xsl:choose element. Instead, you could take advantage of the matching templates to have specific templates for the cases of attributes who names you want to override, and a generic template for other case.
To avoid repetition of code, you could also make the generic template a named template, with a parameter to override the name
<xsl:template match="#*" name="attribute">
<xsl:param name="label" select="local-name()" />
So, the default for most attributes would be to use the attribute name, but the specific template for #FullName could then call this with a different name.
<xsl:template match="#FullName">
<xsl:call-template name="attribute">
<xsl:with-param name="label" select="'Full Name'" />
</xsl:call-template>
</xsl:template>
Here is the full XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="*">
<div class="{local-name()}">
<div> Title: </div>
<xsl:apply-templates select="#*"/>
</div>
</xsl:template>
<xsl:template match="#FullName">
<xsl:call-template name="attribute">
<xsl:with-param name="label" select="'Full Name'" />
</xsl:call-template>
</xsl:template>
<xsl:template match="#*" name="attribute">
<xsl:param name="label" select="local-name()" />
<span class="label">
<xsl:value-of select="concat($label, ' : ')"/>
</span>
<span class="value">
<xsl:value-of select="."/>
</span>
<br/>
</xsl:template>
</xsl:stylesheet>
When applied to the following XML:
<Member TextRank="unknown" ID="12" FullName="My Name" Dob="01/01/1970" />
The following is output:
<div class="Member">
<div> Title: </div>
<span class="label">TextRank : </span>
<span class="value">unknown</span>
<br>
<span class="label">ID : </span>
<span class="value">12</span>
<br>
<span class="label">Full Name : </span>
<span class="value">My Name</span>
<br>
<span class="label">Dob : </span>
<span class="value">01/01/1970</span>
<br>
</div>
This is the solution I came up with.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" />
<xsl:template match="*">
<xsl:element name="div">
<xsl:attribute name="class">className</xsl:attribute>
<div>
Title:
</div>
<!-- UID, Name, DBO-->
<xsl:apply-templates select="#ID"/>
<xsl:apply-templates select="#FullName"/>
<xsl:apply-templates select="#Dob"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:element name="span">
<xsl:attribute name="class">label</xsl:attribute>
<xsl:choose>
<xsl:when test="name() = 'FullName'">
Full Name
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name()"/>
</xsl:otherwise>
</xsl:choose>
:
</xsl:element>
<xsl:element name="span">
<xsl:attribute name="class">value</xsl:attribute>
<xsl:value-of select="."/>
</xsl:element>
<br/>
</xsl:template>
</xsl:stylesheet>
My XML file is as follows:
<worksheet>
<row>
<rowTitle>RT1</rowTitle>
<rowType>yesorno</rowType>
<subLine>subLine1Content</subLine>
<subLine>subLine2Content</subLine>
</row>
<row>
<rowTitle>RT2</rowTitle>
<rowType>operation</rowType>
<subLine>subLine1Content</subLine>
<subLine>subLine2Content</subLine>
<subLine>subLine3Content</subLine>
</row>
.
.
</worksheet>
in my xsl, while displaying contents of a particular row, i'd like to add a class to the html element that'll specify the type of the row it is. I tried using xsl:choose and assigning value to a xsl:variable, but that doesn't work.
I'm trying to display the row as
<ol>
<li class="rowTypeBoolean">
RT1
<ul><li>subLineContent1</li>
<li>subLineContent2</li></ul>
</li>
<li class="rowTypeOptions">
RT2
<ul><li>subLineContent1</li>
<li>subLineContent2</li>
<li>subLineContent3</li></ul>
</li>
.
.
</ol>
XSL file snippet
<xsl:template match="row">
<li class="rowClass ${className}">
<xsl:choose>
<xsl:when test="type = 'YESORNO'">
<xsl:variable name="className" select="rowTypeBoolean"/>
</xsl:when>
<xsl:when test="type = 'OPTIONS'">
<xsl:variable name="className" select="rowTypeOptions"/>
</xsl:when>
<xsl:when test="type = 'OPERATION'">
<xsl:variable name="className" select="rowTypeOperation"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="className" select="rowTypeOther"/>
</xsl:otherwise>
</xsl:choose>
<span class="rowTitleClass">
<xsl:value-of select="rowtitle"/>
</span>
<br/>
<ul class="subLineListClass">
<xsl:apply-templates select="subLine"/>
</ul>
</li>
</xsl:template>
You need to add it as an attribute to the element:
<li>
<xsl:choose>
<xsl:when test="type = 'YESORNO'">
<xsl:attribute name="className">rowTypeBoolean</xsl:attribute>
</xsl:when>
<xsl:when test="type = 'OPTIONS'">
<xsl:attribute name="className">rowTypeOptions</xsl:attribute>
</xsl:when>
<xsl:when test="type = 'OPERATION'">
<xsl:attribute name="className">rowTypeOperation"</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="className">rowTypeOther"</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</li>
The most naive solution would be to use xsl:choose instruction like this:
<li>
<xsl:attribute name="className">
<xsl:choose>
<xsl:when test="type = 'YESORNO'">rowTypeBoolean</xsl:when>
<xsl:when test="type = 'OPTIONS'">rowTypeOptions</xsl:when>
<xsl:when test="type = 'OPERATION'">rowTypeOperation</xsl:when>
<xsl:otherwise>rowTypeOther</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</li>
Other way would be to have an inline map (or via fn:document()) like:
<li class="{$map[#type = current()/type]|$map[not(#type)]}"/>
With this as top level element
<map:map xmlns:map="map">
<item type="YESORNO">rowTypeBoolean</item>
<item type="OPTIONS">rowTypeOption</item>
<item type="OPERATIONS">rowTypeOperation</item>
<item>rowTypeOther</item>
</map:map>
<xsl:variable name="map" select="document('')/*/map:map/*" xmlns:map="map"/>
It isn't necessary at all to use <xsl:choose> in the transformation:
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:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="worksheet">
<ol>
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template match="row[rowType='yesorno']">
<li class="rowTypeBoolean">
<xsl:apply-templates/>
</li>
</xsl:template>
<xsl:template match="row[rowType='operation']">
<li class="rowTypeOperation">
<xsl:apply-templates/>
</li>
</xsl:template>
<xsl:template match="row[rowType='options']">
<li class="rowTypeOptions">
<xsl:apply-templates/>
</li>
</xsl:template>
<xsl:template match="row">
<li class="rowTypeOther">
<xsl:apply-templates/>
</li>
</xsl:template>
<xsl:template match="subLine[1]">
<ul>
<xsl:apply-templates select="../subLine" mode="process"/>
</ul>
</xsl:template>
<xsl:template match="subLine" mode="process">
<li><xsl:apply-templates/></li>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<worksheet>
<row>
<rowTitle>RT1</rowTitle>
<rowType>yesorno</rowType>
<subLine>subLine1Content</subLine>
<subLine>subLine2Content</subLine>
</row>
<row>
<rowTitle>RT2</rowTitle>
<rowType>operation</rowType>
<subLine>subLine1Content</subLine>
<subLine>subLine2Content</subLine>
<subLine>subLine3Content</subLine>
</row>
</worksheet>
produces the wanted, correct result:
<ol>
<li class="rowTypeBoolean">
<rowTitle>RT1</rowTitle>
<rowType>yesorno</rowType>
<ul>
<li>subLine1Content</li>
<li>subLine2Content</li>
</ul>
<subLine>subLine2Content</subLine>
</li>
<li class="rowTypeOperation">
<rowTitle>RT2</rowTitle>
<rowType>operation</rowType>
<ul>
<li>subLine1Content</li>
<li>subLine2Content</li>
<li>subLine3Content</li>
</ul>
<subLine>subLine2Content</subLine>
<subLine>subLine3Content</subLine>
</li>
</ol>
Do note:
Only simple templates and <xsl:apply-templates> are used. Therefore, the chances of committing an error are minimized.
This code is inherently extensible and maintainable. If a new row type is introduced none of the existing templates will need to be altered -- just a new short and simple template will need to be added, that matches a row element having a rowType child with the new value.
The mapping between rowType and the corresponding value of the class attribute can be specified in a special global-level namespaced element and/or variable (as done in Alejandro's solution), or even in a separate XML document. Then we can have just one single template, matching row.