I am new to xslt. I want below input to be converted into output shown below:
Input:
<ATTRIBUTE-VALUE>
<THE-VALUE>
<div xmlns="http://www.w3.org/1999/xhtml">
<h1 dir="ltr" id="_1536217498885">Main Description</h1>
Line1 The main description text goes here.
<p>Line2 The main description text goes here.</p>
<p>Line3 The main description text goes here.</p>
<p><img alt="Embedded Image" class="embeddedImageLink" id="_1536739954166" src="_9c3778a0-d596-4eef-85fa-052a5e1b2166.jpg"/></p>
<h1 dir="ltr" id="_1536217498886">Key Consideration</h1>
<p>Line1 The key consideration text goes here.</p>
<p>Line2 The key consideration text goes here.</p>
<h1 dir="ltr" id="_1536217498887">Skills</h1>
<p>Line1 The Skills text goes here.</p>
<p>Line2 The Skills text goes here.</p>
<p>Line3 The Skills text goes here.</p>
<h1 dir="ltr" id="_1536217498888">Synonyms</h1>
<p>The Synonyms text goes here.</p>
</div>
</THE-VALUE>
</ATTRIBUTE-VALUE>
Output should be:
<MainDescription>
<![CDATA[
<p>Line1 The main description text goes here.</p>
<p>Line2 The main description text goes here.</p>
<p>Line3 The main description text goes here.</p>
<p><img alt="Embedded Image" class="embeddedImageLink" id="_1536739954166" src="_9c3778a0-d596-4eef-85fa-052a5e1b2166.jpg"/></p>
]]>
</MainDescription>
<KeyConsiderations>
<![CDATA[
<p>Line1 The key consideration text goes here.</p>
<p>Line2 The key consideration text goes here.</p>
]]>
</KeyConsiderations>
<Skills>
<p>Line1 The Skills text goes here.</p>
<p>Line2 The Skills text goes here.</p>
<p>Line3 The Skills text goes here.</p>
</Skills>
<Synonyms>
<p>The Synonyms text goes here.</p>
</Synonyms>
I want the data between <h1> and it can contain any html tag that should be generated in output. I tried the code at: https://xsltfiddle.liberty-development.net/bdxtqy/2. But it gives the data only if data is included under html tags. Please provide pointers on how to achieve required output.
XSL code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="xhtml exsl"
version="1.0">
<xsl:import href="http://lenzconsulting.com/xml-to-string/xml-to-string.xsl"/>
<xsl:output method="xml" indent="yes"
cdata-section-elements="MainDescription KeyConsideration"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="h1-group" match="xhtml:div/*[not(self::xhtml:h1)]" use="generate-id(preceding-sibling::xhtml:h1[1])"/>
<xsl:template match="xhtml:div[xhtml:h1]">
<xsl:apply-templates select="xhtml:h1"/>
</xsl:template>
<xsl:template match="xhtml:h1">
<xsl:element name="{translate(., ' ', '')}">
<xsl:variable name="rtf-with-xhtml-ns-stripped">
<xsl:apply-templates select="key('h1-group', generate-id())"/>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($rtf-with-xhtml-ns-stripped)/node()" mode="xml-to-string"/>
</xsl:element>
</xsl:template>
<xsl:template match="xhtml:p">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
</xsl:stylesheet>
I am getting output as:
<ATTRIBUTE-VALUE>
<THE-VALUE>
<MainDescription><![CDATA[<p>Line2 The main description text goes here.</p><p><img alt="Embedded Image" class="embeddedImageLink" id="_1536739954166" src="_9c3778a0-d596-4eef-85fa-052a5e1b2166.jpg" xmlns="http://www.w3.org/1999/xhtml"/></p>]]></MainDescription>
<KeyConsideration><![CDATA[<p>Line1 The key consideration text goes here.</p><p>Line2 The key consideration text goes here.</p>]]></KeyConsideration>
<Skills><p>Line1 The Skills text goes here.</p><p>Line2 The Skills text goes here.</p><p>Line3 The Skills text goes here.</p></Skills>
<Synonyms />
</THE-VALUE>
</ATTRIBUTE-VALUE>
If you change the code to match on node() instead of * for elements you get the text nodes included:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="xhtml exsl"
version="1.0">
<xsl:import href="http://lenzconsulting.com/xml-to-string/xml-to-string.xsl"/>
<xsl:output method="xml" indent="yes"
cdata-section-elements="MainDescription KeyConsideration"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="h1-group" match="xhtml:div/node()[not(self::xhtml:h1)]" use="generate-id(preceding-sibling::xhtml:h1[1])"/>
<xsl:template match="xhtml:div[xhtml:h1]">
<xsl:apply-templates select="xhtml:h1"/>
</xsl:template>
<xsl:template match="xhtml:h1[. = 'Main Description' or . = 'Key Consideration']">
<xsl:element name="{translate(., ' ', '')}">
<xsl:variable name="rtf-with-xhtml-ns-stripped">
<xsl:apply-templates select="key('h1-group', generate-id())"/>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($rtf-with-xhtml-ns-stripped)/node()" mode="xml-to-string"/>
</xsl:element>
</xsl:template>
<xsl:template match="xhtml:h1">
<xsl:element name="{translate(., ' ', '')}">
<xsl:variable name="rtf-with-xhtml-ns-stripped">
<xsl:apply-templates select="key('h1-group', generate-id())"/>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($rtf-with-xhtml-ns-stripped)/node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:template>
<xsl:template match="xhtml:p">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bdxtqy/41
It is not clear when/where you want to wrap plain text like Line1 The main description text goes here. into a p element.
For the CDATA section use of disable-output-escaping I think you need to override the template for text() nodes of the imported xml-to-string stylesheet:
<xsl:template match="text()" mode="xml-to-string">
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:template>
https://xsltfiddle.liberty-development.net/bdxtqy/42
I haven't tested whether that breaks anything.
Related
I want to insert an html snippet from an external file into my output document with copy-of like described here: https://stackoverflow.com/a/5976762/18427492
The html snipped is a navigation bar and also used by other (python) scripts to generate other html files.
I need to replace the path in "href" to match a relative path that i have in a XSLT variable.
Full file content (Template file to be copied):
<ul class="nav">
<li class="fineprint">MyNiceGame Developer Mode Documentation</li>
<li class="switchlang"><img src="/deco/dco_en_sml.gif" alt="English" border="0"></img></li>
<li>Introduction</li>
<li>Contents</li>
<li>Search</li>
<li>Engine</li>
<li>Command Line</li>
<li>Game Data</li>
<li>Script</li>
</ul>
So how can i insert this snippet into my XSL document and replace ../../sdk/ (its possible to change this string to something like {replace-me}/sdk/...) with a relative path that i already have in a XSLT variable?
My XSLT document (i want to replace the <xsl:call-template name="nav"/> with the template file processing):
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0" xpath-default-namespace="https://clonkspot.org" exclude-result-prefixes="xs">
<xsl:output method="html" encoding="ISO-8859-1" doctype-public="-//W3C//DTD HTML 4.01//EN"
doctype-system="http://www.w3.org/TR/html4/strict.dtd"/>
<xsl:template match="/clonkDoc">
<html>
<body>
<xsl:call-template name="nav"/>
<xsl:apply-templates select="func"/>
<!-- other possible nodes under /clonkDoc -->
<xsl:call-template name="nav"/>
</body>
</html>
</xsl:template>
<xsl:template name="nav">
<xsl:param name="relpath" tunnel="yes"/>
<ul class="nav">
<li class="fineprint">
<xsl:when test='lang("en")'>>MyNiceGame Developer Mode Documentation</xsl:when>
</li>
<!-- Other li elements -->
</xsl:template>
Example source file:
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<clonkDoc xmlns="https://clonkspot.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://clonkspot.org ../../../clonk.xsd" xml:lang="de">
<func>
<!-- other nodes -->
</func>
</clonkDoc>
Desired target file:
<!DOCTYPE html
PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<!-- stuff -->
</head>
<body>
<ul class="nav">
<!-- The corrected li elements with modified a href link -->
</ul>
<!-- Other stuff from source file (<func>) -->
<ul class="nav">
<!-- The corrected li elements with modified a href link -->
</ul>
</body>
</html>
Martin Honnen's solution for my specific case with the xpath-default-namespace:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0" xpath-default-namespace="https://clonkspot.org" exclude-result-prefixes="xs">
<xsl:output method="html" encoding="ISO-8859-1" doctype-public="-//W3C//DTD HTML 4.01//EN"
doctype-system="http://www.w3.org/TR/html4/strict.dtd"/>
<xsl:template match="/clonkDoc">
<html>
<body>
<xsl:apply-templates select="doc('file.html')//ul[#class = 'nav']" xpath-default-namespace="" mode="fix-links"/>
<xsl:apply-templates select="func"/>
<!-- other possible nodes under /clonkDoc -->
<xsl:apply-templates select="doc('file.html')//ul[#class = 'nav']" xpath-default-namespace="" mode="fix-links"/>
</body>
</html>
</xsl:template>
<xsl:mode name="fix-links" on-no-match="shallow-copy"/>
<xsl:template mode="fix-links" match="ul/li/a/#href" xpath-default-namespace="">
<xsl:message>Value href: <xsl:value-of select="."></xsl:value-of></xsl:message>
<xsl:attribute name="{name()}" select="replace(., '../../sdk', 'foobar')"/>
</xsl:template>
copy-of makes a a deep copy, if you want to transform input nodes (even only their attribute values) you write templates to do so e.g. <xsl:apply-templates select="doc('file.xml')//ul[#class = 'nav']" mode="fix-links"/>, or, perhaps, as the edit says the snippet with the ul is all in the file, use simply <xsl:apply-templates select="doc('file.xml')" mode="fix-links"/>, and
<xsl:mode name="fix-links" on-no-match="shallow-copy"/>
<xsl:template mode="fix-links" match="ul/li/a/#href">
<xsl:attribute name="{name()}" select="replace(., '../../sdk', $varname)"/>
</xsl:template>
The xsl:mode declaration is XSLT 3 only, in earlier versions declare the identity transformation for that mode e.g.
<xsl:template mode="fix-links" match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" mode="fix-links"/>
</xsl:copy>
</xsl:template>
in XSLT 1 or
<xsl:template mode="fix-links" match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" mode="#current"/>
</xsl:copy>
</xsl:template>
in XSLT 2.
XSLT 3 sample (slighly adapted for the demonstration to work with the primary input) outputs
<ul class="nav">
<li class="fineprint">MyNiceGame Developer Mode Documentation</li>
<li class="switchlang"><img src="/deco/dco_de_sml.gif" alt="German" border="0"/></li>
<li>Introduction</li>
<li>Contents</li>
<li>Search</li>
<li>Engine</li>
<li>Command Line</li>
<li>Game Data</li>
<li>Script</li>
</ul>
As for the information in the latest edit that the secondary input document you want to process has elements in no namespace but your primary one has elements in a certain namespace that your XSLT has used as the xpath-default-namespace, in that case you need to override that for any selections in the secondary input e.g.
<xsl:mode name="fix-links" on-no-match="shallow-copy"/>
<xsl:template mode="fix-links" match="ul/li/a/#href" xpath-default-namespace="">
<xsl:attribute name="{name()}" select="replace(., '../../sdk', $varname)"/>
</xsl:template>
and if you continue to use the apply-templates with an element selector, there as well e.g. <xsl:apply-templates select="doc('file.xml')//ul[#class = 'nav']" xpath-default-namespace="" mode="fix-links"/>.
I want to Select various text in the same node and put them to different nodes in XSLT.
Input :
<p>The first para
<formula>formula text</formula> The second para
<list>List text</list>
The third para </p>
Desired output :
<pcom>The first para</pcom>
<formulai>formula text</formulai>
<pi>The second para</pi>
<listi>List text</listi>
<pi>The third para</pi>
Tried code :
<xsl:template match="p/text()[preceding-sibling::formula or preceding-sibling::list]">
<pi><xsl:apply-template/></pi>
</xsl:template>
<p>The first para</p> , <p>The second para</p> and <p>The third para</p> are text in same <p>. I want to tarns form them to seperate <pi>.
Those text preceding-sibling must be <formula> or <list>. If preceding-sibling not a <formula> or <list> then the output should be in <pcom>
How can I solve this? I am using XSLT 2.0
This should work for the given example:
<xsl:template match="p/text()">
<pcom>
<xsl:copy/>
</pcom>
</xsl:template>
<xsl:template match="p/text()[preceding-sibling::formula or preceding-sibling::list]">
<pi>
<xsl:copy/>
</pi>
</xsl:template>
However, the problem description is ambiguous. If you want to limit the pi element to text nodes whose immediately preceding sibling is formula or list, use:
<xsl:template match="p/text()[preceding-sibling::node()[1][self::formula or self::list]]">
<pi>
<xsl:copy/>
</pi>
</xsl:template>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()[ following-sibling::*[1][self::list] and preceding-sibling::*[1][self::formula] or preceding-sibling::*[1][self::list]]">
<pi>
<xsl:value-of select=" normalize-space(.)"/>
</pi>
</xsl:template>
<xsl:template match="node()[following-sibling::*[1][self::formula]]">
<pcom>
<xsl:value-of select="normalize-space(.)"/>
</pcom>
</xsl:template>
<xsl:template match="formula">
<formulai>
<xsl:value-of select="."/>
</formulai>
</xsl:template>
<xsl:template match="p">
<xsl:apply-templates/>
</xsl:template>
You may also try this.
I want to get the first heading (h1) before a table in a docx.
I can get all headings with:
<xsl:template match="w:p[w:pPr/w:pStyle[#w:val='berschrift1']]">
<p>
<context>
<xsl:value-of select="." />
</context>
</p>
</xsl:template>
and I can also get all tables
<xsl:template match="w:tbl">
<p>
<table>
<xsl:value-of select="." />
</table>
</p>
</xsl:template>
But unfortunetly the processor does not accept
<xsl:template match="w:tbl/preceding-sibling::w:p[w:pPr/w:pStyle[#w:val='berschrift1']]">
<p>
<table>
<xsl:value-of select="." />
</table>
</p>
</xsl:template>
Here is a reduced XML file extracted from a docx: http://pastebin.com/KbUyzRVv
I want something like that as a result:
<context>Let’s get it on</context> <- my heading
<table>data</table>
<context>Let’s get it on</context> <- my heading
<table>data</table>
<context>We’re in the middle of something</context> <- my heading
<table>data</table>
Thanks to Daniel Haley I was able to find a solution for that problem. I'll post it here, so it is independend of the pastebin I postet below.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:v="urn:schemas-microsoft-com:vml" exclude-result-prefixes="xsl w v">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="w:tbl">
<context>
<xsl:value-of select="(preceding-sibling::w:p[w:pPr/w:pStyle[#w:val = 'berschrift1']])[last()]"/>
</context>
<table>
<xsl:value-of select="."/>
</table>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Hard to answer without a Minimal, Complete, and Verifiable example, but try this:
<xsl:template match="w:tbl">
<p>
<table>
<xsl:value-of select="(preceding::w:p[w:pPr/w:pStyle[#w:val='berschrift1']])[last()]"/>
</table>
</p>
</xsl:template>
Assuming you can use XSLT 2.0 (and most people can, nowadays), I find a useful technique here is to have a global variable that selects all the relevant nodes:
<xsl:variable name="special"
select="//w:tbl/preceding-sibling::w:p[w:pPr/w:pStyle[#w:val='berschrift1']][1]"/>
and then use this variable in a template rule:
<xsl:template match="w:p[. intersect $special]"/>
In XSLT 3.0 you can reduce this to
<xsl:template match="$special"/>
I've the following XML.
<?xml version="1.0" encoding="UTF-8"?>
<docs>
<biblos>
<texto xmlns="http://www.aranzadi.es/namespace/contenido/biblos/texto" idioma="spa">
<parrafo>
<en-origen estilo-fuente="cursiva">This is cursive text.</en-origen>
</parrafo>
</texto>
</biblos>
</docs>
and the following XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<section class="chapter">
<xsl:apply-templates select="docs"/>
</section>
</body>
</html>
</xsl:template>
<xsl:template match="docs">
<div class="chapter">
<xsl:text>Docs Block</xsl:text>
<xsl:apply-templates select="biblos"/>
</div>
</xsl:template>
<xsl:template match="biblos">
<xsl:text>biblos block</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="texto">
<xsl:text>Text To Block</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="parrafo">
<div class="para">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="parrafo">
<span class="format-smallcaps">
<xsl:apply-templates/>
</span>
</xsl:template>
<xsl:template match="en-origen">
<xsl:variable name="fontStyle">
<xsl:choose>
<xsl:when test="./#estilo-fuente">
<xsl:value-of select="concat('font-style-',#estilo-fuente)"/>
</xsl:when>
<xsl:when test="./#format">
<xsl:value-of select="concat('format-',#format)"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
<span class="{$fontStyle}">
<xsl:value-of select="."/>
<xsl:apply-templates select="para"/>
</span>
</xsl:template>
</xsl:transform>
when i run this, I'm getting the below output.
<!DOCTYPE html
PUBLIC "XSLT-compat">
<html>
<body>
<section class="chapter">
<div class="chapter">Docs Blockbiblos block
This is cursive text.
</div>
</section>
</body>
</html>
Here the problem is, though I've declared texto and child nodes of it in my XSLT, it is not getting called, but the text is directly getting printed.
Please let me know where I'm going wrong and how I can fix it.
Good question (thanks for providing a complete, working example!). Often, if elements are not matched, the cause lies in missing namespaces:
You have the following in your input XML:
<texto xmlns="http://www.aranzadi.es/namespace/contenido/biblos/texto" idioma="spa">
In other words, the texto element is in a namespace. In your XSLT you have the following:
<xsl:template match="texto">
Since no namespace is declared for XPath (xpath-default-namespace on the containing xsl:template or xsl:stylesheet), this will operate on elements texto in no namespace, meaning, as written, it will not match texto from your source.
You can solve this by:
<xsl:transform
xmlns:tto="http://www.aranzadi.es/namespace/contenido/biblos/texto"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
and:
<xsl:template match="tto:texto">
Now your template will be matched.
Remember that element names can be in a namespace if the namespace is declared on that element, but the attributes, unless prefixes, are in no nammespace, so this solution is only required (given your example input) on matching or selecting elements.
Also, it is important to realize that prefixes do not matter, they do not need to match the prefix (or absence thereof) from the source document. What matters, is that the namespace bound to the prefix matches.
If there are child elements, in this case parrafo and en-origen, these inherit the namespace given on their parent element. So if you want to match these elements as well, you should adjust their names to tto:paraffo and tto:en-origin in patterns and XPath expressions.
Input:
<a q='r'>
<b x='1' y='2' z='3'/>
<!-- other a content -->
</a>
Desired output:
<A q='r' x='1' y='2' z='3'>
<!-- things derived from other a content, no b -->
</A>
Could someone kindly give me a recipe?
Easy.
<xsl:template match="a">
<A>
<xsl:copy-of select="#*|b/#*" />
<xsl:apply-templates /><!-- optional -->
</A>
</xsl:template>
The <xsl:apply-templates /> is not necessary if you have no further children of <a> you want to process.
Note
the use of <xsl:copy-of> to insert source nodes into the output unchanged
the use of the union operator | to select several unrelated nodes at once
that you can copy attribute nodes to a new element as long as it is the first thing you do - before you add any child elements.
EDIT: If you need to narrow down which attributes you copy, and which you leave alone, use this (or a variation of it):
<xsl:copy-of select="(#*|b/#*)[
name() = 'q' or name() = 'x' or name() = 'y' or name() = 'z'
]" />
or even
<xsl:copy-of select="(#*|b/#*)[
contains('|q|x|y|z|', concat('|', name(), '|'))
]" />
Note how the parentheses make the predicate apply to all matched nodes.
XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a">
<A>
<xsl:apply-templates select="#*|b/#*|node()"/>
</A>
</xsl:template>
<xsl:template match="b"/>
</xsl:stylesheet>
output
<A q="r" x="1" y="2" z="3"><!-- other a content --></A>