Create new element in xslt by appending the attribute value with it - xslt-2.0

<sec id="abg-c42-s1-1" disp-level="2">
<title>Introduction</title>
Problem :
I have to check the disp-level attribute and replace < title> tag with < h>.
Example: If the value of disp-level is "1" then < title> tag is replaced by < h1>.
If the value of disp-level is "3" then < title> tag is replaced by < h3> and so on.
Any solutions?
Thanks...

Well, write a template matching sec[#disp-level]/title that does that:
<xsl:template match="sec[#disp-level]/title">
<xsl:element name="h{../#disp-level}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
As for most stylesheets, this approach works and easily allows further modular transformations of other elements or nodes if the stylesheet includes the identity transformation template
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
or in XSLT 3 the declaration <xsl:mode on-no-match="shallow-copy"/>.

Related

XSLT- Pre Define NameSpace

Hi I have an XML that upon each delivery has a different unique named Namespace that I cannot pre determine with standard processes.
<ABC xmlns:this="urn:uuid:9b1f15a9-69de-11d2-b6bc-fcab70ff7331" version="1.1">
<Extensions>
<Identification>urn:uuid:9b1f15a9-69de-11d2-b6bc-fcab70ff7331</Identification>
<Extension>
<SrcPackage>
<this:ABDList>
<TaggedValue>111</TaggedValue>
</this:ABDList>
<this:SubBegin>0</this:SubBegin>
</SrcPackage>
<MatPackage>
<this:ABDList>
<TaggedValue>222</TaggedValue>
</this:ABDList>
<this:SubBegin>1</this:SubBegin>
</MatPackage>
<!-- Stuff -->
</Extention>
</Extentions>
</ABC>
The Next XML delivered could be
<ABC xmlns:this="urn:uuid:9b1FFae4-69de-11d2-b6bc-fcab70ff7331" version="1.1">
<Extensions>
<Identification>urn:uuid:9b1FFae4-69de-11d2-b6bc-fcab70ff7331</Identification>
<Extension>
<SrcPackage>
<this:ABDList>
<TaggedValue>333</TaggedValue>
</this:ABDList>
<this:SubBegin>0</this:SubBegin>
</SrcPackage>
<MatPackage>
<this:ABDList>
<TaggedValue>444</TaggedValue>
</this:ABDList>
<this:SubBegin>1</this:SubBegin>
</MatPackage>
<!-- Stuff -->
</Extention>
</Extentions>
</ABC>
My current XSL stylesheet works on the first XML predefining the Namespace
But I am looking to find a way to re-define it later on. on the process. I have added a variable to pull the relevant uuid from the Identification element but am not sure how to integrate this. Using the below stylesheet to process any other XML results in false results.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:this="urn:uuid:9b1f15a9-69de-11d2-b6bc-fcab70ff7331"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:variable name="SelOpGroup" select="/ABC/Extensions/Identification"/>
<!-- Pass thru --->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/ABC/Extensions/SrcPackage>
<xsl:copy>
<this:ABDList>
<xsl:copy-of select ="this:ABDList/*"/>
<TaggedA>888</TaggedA>
</this:ABDList>
<this:SubBegin><xsl:value-of select="somethingelse"/> </this:SubBegin>
</xsl:copy>
</xsl:template>
<xsl:template match="/ABC/Extensions/MatPackage>
<xsl:copy>
<this:ABDList>
<xsl:copy-of select ="this:ABDList/*"/>
<TaggedB>999</TaggedB>
</this:ABDList >
<this:SubBegin><xsl:value-of select="somethingelse"/> </this:SubBegin>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Expected Result
<ABC xmlns:this="urn:uuid:9b1FFae4-69de-11d2-b6bc-fcab70ff7331" version="1.1">
<Extensions>
<Identification>urn:uuid:9b1FFae4-69de-11d2-b6bc-fcab70ff7331</Identification>
<Extension>
<SrcPackage>
<this:ABDList>
<TaggedValue>333</TaggedValue>
<TaggedA>888</TaggedA>
</this:ABDList>
<this:SubBegin>a value</this:SubBegin>
</SrcPackage>
<MatPackage>
<this:ABDList>
<TaggedValue>444</TaggedValue>
<TaggedB>999</TaggedA>
</this:ABDList>
<this:SubBegin>a value</this:SubBegin>
</MatPackage>
<!-- Stuff -->
</Extention>
</Extentions>
</ABC>
Many thanks,
Adrian
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:variable name="SelOpGroup" select="/ABC/Extensions/Identification"/>
<!-- Pass thru -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Extension/SrcPackage">
<xsl:copy>
<xsl:element name="{'this:ABDList'}" namespace="{$SelOpGroup}">
<xsl:copy-of select="/*/namespace::*[name()='this']"/>
<xsl:copy-of select ="*[name() = 'this:ABDList']/*"/>
<TaggedA>888</TaggedA>
</xsl:element>
<xsl:element name="{'this:SubBegin'}" namespace="{$SelOpGroup}">
<xsl:copy-of select="/*/namespace::*[name()='this']"/>
<xsl:value-of select="'somethingelse'"/>
</xsl:element>
</xsl:copy>
</xsl:template>
<xsl:template match="Extension/MatPackage">
<xsl:copy>
<xsl:element name="{'this:ABDList'}" namespace="{$SelOpGroup}">
<xsl:copy-of select="/*/namespace::*[name()='this']"/>
<xsl:copy-of select ="*[name() = 'this:ABDList']/*"/>
<TaggedB>999</TaggedB>
</xsl:element>
<xsl:element name="{'this:SubBegin'}" namespace="{$SelOpGroup}">
<xsl:copy-of select="/*/namespace::*[name()='this']"/>
<xsl:value-of select="'somethingelse'"/>
</xsl:element>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied on the first provided XML document:
<ABC xmlns:this="urn:uuid:9b1f15a9-69de-11d2-b6bc-fcab70ff7331" version="1.1">
<Extensions>
<Identification>urn:uuid:9b1f15a9-69de-11d2-b6bc-fcab70ff7331</Identification>
<Extension>
<SrcPackage>
<this:ABDList>
<TaggedValue>111</TaggedValue>
</this:ABDList>
<this:SubBegin>0</this:SubBegin>
</SrcPackage>
<MatPackage>
<this:ABDList>
<TaggedValue>222</TaggedValue>
</this:ABDList>
<this:SubBegin>1</this:SubBegin>
</MatPackage>
<!-- Stuff -->
</Extension>
</Extensions>
</ABC>
Produces the wanted, correct result:
<ABC xmlns:this="urn:uuid:9b1f15a9-69de-11d2-b6bc-fcab70ff7331" version="1.1">
<Extensions>
<Identification>urn:uuid:9b1f15a9-69de-11d2-b6bc-fcab70ff7331</Identification>
<Extension>
<SrcPackage>
<this:ABDList>
<TaggedValue>111</TaggedValue>
<TaggedA>888</TaggedA>
</this:ABDList>
<this:SubBegin>somethingelse</this:SubBegin>
</SrcPackage>
<MatPackage>
<this:ABDList>
<TaggedValue>222</TaggedValue>
<TaggedB>999</TaggedB>
</this:ABDList>
<this:SubBegin>somethingelse</this:SubBegin>
</MatPackage><!-- Stuff -->
</Extension>
</Extensions>
</ABC>
When the same transformation is applied on the second provided XML document:
<ABC xmlns:this="urn:uuid:9b1FFae4-69de-11d2-b6bc-fcab70ff7331" version="1.1">
<Extensions>
<Identification>urn:uuid:9b1FFae4-69de-11d2-b6bc-fcab70ff7331</Identification>
<Extension>
<SrcPackage>
<this:ABDList>
<TaggedValue>333</TaggedValue>
</this:ABDList>
<this:SubBegin>0</this:SubBegin>
</SrcPackage>
<MatPackage>
<this:ABDList>
<TaggedValue>444</TaggedValue>
</this:ABDList>
<this:SubBegin>1</this:SubBegin>
</MatPackage>
<!-- Stuff -->
</Extension>
</Extensions>
</ABC>
Again the wanted, correct result is produced:
<ABC xmlns:this="urn:uuid:9b1FFae4-69de-11d2-b6bc-fcab70ff7331" version="1.1">
<Extensions>
<Identification>urn:uuid:9b1FFae4-69de-11d2-b6bc-fcab70ff7331</Identification>
<Extension>
<SrcPackage>
<this:ABDList>
<TaggedValue>333</TaggedValue>
<TaggedA>888</TaggedA>
</this:ABDList>
<this:SubBegin>somethingelse</this:SubBegin>
</SrcPackage>
<MatPackage>
<this:ABDList>
<TaggedValue>444</TaggedValue>
<TaggedB>999</TaggedB>
</this:ABDList>
<this:SubBegin>somethingelse</this:SubBegin>
</MatPackage><!-- Stuff -->
</Extension>
</Extensions>
</ABC>
This is bizarre input (what were they smoking?). But since the namespace is only used on one element, ABDList, my approach would be to select the ABDList elements using *:ABDList in XSLT 2.0, or *[local-name()='ABDList'] in XSLT 1.0.
I have an XML that upon each delivery has a different unique named
Namespace
Someone ahead of you obviously does not understand the purpose of having a namespace.
Perhaps this could work for your unfortunate situation:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="SrcPackage/*/TaggedValue">
<xsl:copy-of select="."/>
<TaggedA>888</TaggedA>
</xsl:template>
<xsl:template match="MatPackage/*/TaggedValue">
<xsl:copy-of select="."/>
<TaggedB>999</TaggedB>
</xsl:template>
</xsl:stylesheet>

tagging words using lookup, what's the best approach?

I have some strings I want to test to see if they contain specific words. The words in question are in a lookup node, and if there is a match the word in the string needs to be tagged. I have a script that works almost ok, but I want to know if I'm using the best format, as I believe it's rather resource consuming, and not very foolproof.
Example xml :
<Main>
<NTUS>
<NTU>match</NTU>
<NTU>test</NTU>
</NTUS>
<Folder id="update">
<about>This content is not in a span so we ignore it completely, even if we would have a match</about>
<Title>
<span class="string simple" lang="en">Some test content containing a single match</span>
</Title>
<Content>
<span class="string complex" lang="en">Also keywords in sub elements should <strong>pass the test</strong>, and match.</span>
</Content>
</Folder>
</Main>
My current xslt :
<xsl:param name="units">
<xsl:copy-of select="//NTU"/>
</xsl:param>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="NTUS"/>
<xsl:template match="text()[ancestor::span]">
<xsl:analyze-string select="." regex="\s+">
<xsl:matching-substring>
<xsl:value-of select="."/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:variable name="theWord" select="."/>
<xsl:choose>
<xsl:when test="$units/*[text()=$theWord]">
<ntu>
<xsl:value-of select="."/>
</ntu>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
results in following :
<Main>
<Folder id="update">
<about>This content is not in a span so we ignore it completely, even if we would have a match</about>
<Title>
<span class="string simple" lang="en">Some <ntu>test</ntu> content containing a single <ntu>match</ntu></span>
</Title>
<Content>
<span class="string complex" lang="en">Also keywords in sub elements should <strong>pass the <ntu>test</ntu></strong>, and match.</span>
</Content>
</Folder>
</Main>
Which is almost ok apart from the last node, as the [match] is at the end of the sentence and therefore not passing the regex. I can adjust it to make it match, but it could become pretty complex then, so i want to know if there are better ways to address this problem.
EDIT : there seems to be a small misbehaviour when you use a comma delimited list (might be on other occasions also, but this one I noticed)...
So for instance following xml
<Main>
<NTUS>
<NTU>OPTION1</NTU>
<NTU>OPTION2</NTU>
<NTU>OPTION3</NTU>
<NTU>OPTION4</NTU>
<NTU>OPTION5</NTU>
</NTUS>
<local xml:lang="en">
<span>Test string containing some comma seperarated lookup values: OPTION1, OPTION2, OPTION3, OPTION4, OPTION5</span>
</local>
Returns following when the script is applied :
<span>Test string containing some comma seperarated lookup values: <ntu>OPTION1</ntu>, OPTION2, <ntu>OPTION3</ntu>, OPTION4, <ntu>OPTION5</ntu></span>
so every second match is skipped. Any idea what is causing this behaviour ?
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:variable name="vPatterns" select=
"string-join(/*/NTUS/*, '|')"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="span//text()">
<xsl:analyze-string select="." regex=
"(^|(\P{{L}})+)({$vPatterns})($|(\P{{L}})+)">
<xsl:non-matching-substring><xsl:value-of select="."/></xsl:non-matching-substring>
<xsl:matching-substring>
<xsl:value-of select="regex-group(1)"/>
<ntu><xsl:value-of select="regex-group(3)"/></ntu>
<xsl:value-of select="regex-group(4)"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the provided XML document:
<Main>
<NTUS>
<NTU>match</NTU>
<NTU>test</NTU>
</NTUS>
<Folder id="update">
<about>This content is not in a span so we ignore it completely, even if we would have a match</about>
<Title>
<span class="string simple" lang="en">Some test content containing a single match</span>
</Title>
<Content>
<span class="string complex" lang="en">Also keywords in sub elements should <strong>pass the test</strong>, and match.</span>
</Content>
</Folder>
</Main>
the wanted, correct result is produced:
<Main>
<NTUS>
<NTU>match</NTU>
<NTU>test</NTU>
</NTUS>
<Folder id="update">
<about>This content is not in a span so we ignore it completely, even if we would have a match</about>
<Title>
<span class="string simple" lang="en">Some <ntu>test</ntu> content containing a testmatch or a single <ntu>match</ntu></span>
</Title>
<Content>
<span class="string complex" lang="en">Also keywords in sub elements should <strong>pass the <ntu>test</ntu></strong>, and <ntu>match</ntu>.</span>
</Content>
</Folder>
</Main>

Saxon-ce with multiple user input controls

I am making controls on a web page (comboboxes) with Saxon-CE and Xslt 2.0. I am having trouble in passing the values of multiple control to a template that uses the values from that control to process the document. Here is what I have:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ixsl="http://saxonica.com/ns/interactiveXSLT"
extension-element-prefixes="ixsl">
<xsl:template match="/">
<xsl:result-document href="#comboBox1">
<select id="myBox1">
<option value="1">One</option>
<option value="2">two</option>
</select>
</xsl:result-document>
<xsl:result-document href="#comboBox2">
<select id="myBox2">
<option value="A">Letter-A</option>
<option value="B">Letter-B</option>
</select>
</xsl:result-document>
</xsl:template>
<xsl:template match="select[#id='myBox1'] mode=ixsl:onchange">
<xsl:variable name="control1" select="."/>
<xsl:variable name="numVal" select="ixsl:get($control1,'value')"/>
<xsl:call-template name="displayStuff">
<xsl:with-param name="field1" select="$numVal"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="select[#id='myBox2'] mode=ixsl:onchange">
<xsl:variable name="control2" select="."/>
<xsl:variable name="letVal" select="ixsl:get($control2,'value')"/>
<xsl:call-template name="displayStuff">
<xsl:with-param name="field2" select="$letVal"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="displayStuff">
<xsl:param name="field1" select="0"/>
<xsl:param name="field2" select="Z">
<xsl:result-document href="#display" method="ixsl:replace-content">
<xsl:text>Number: </xsl:text> <xsl:value-of select="$field1"/><br/>
<xsl:text>Letter: </xsl:text> <xsl:value-of select="$field2"/><br/>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>
The problem is that each control returns the correct value to the display template for an then item just changed, but not the other item.
For example if I select 1 in the first dropbox I get Number: 1 Letter: Z
but now if I change the vale of the second dropbox (for lets say to A) I get Number: 0 Letter:A.
How can I make sure what is passed to the display template is the current selected value of all the dropboxes, not the one just changed?
Instead of passing the current values of the controls as parameters to the displayStuff template, why not have that template access them directly from the HTML page by means of XPath expressions?
I suspect you could combine all this into a single template:
<xsl:template match="select[#id=('myBox1', 'myBox2')] mode=ixsl:onchange">
<xsl:variable name="control1" select="."/>
<xsl:variable name="numVal" select="ixsl:get(id('myBox1'),'value')"/>
<xsl:variable name="letVal" select="ixsl:get(id('myBox2'),'value')"/>
<xsl:result-document href="#display" method="ixsl:replace-content">
<xsl:text>Number: </xsl:text> <xsl:value-of select="$numVal"/><br/>
<xsl:text>Letter: </xsl:text> <xsl:value-of select="$letVal"/><br/>
</xsl:result-document>
</xsl:template>

Parent text with child content in the middle of it

I have this XML code
<parag>blah blah blah</parag>
<parag>blah blah, refer to <linkCode code1="a" code2="b" code3="c"/> for further details.</parag>
I can't figure out how to get the link to stay in the middle of the parent text. The following code
<xsl:for-each select="parag">
<p><xsl:value-of select="text()"/>
<xsl:for-each select="linkCode">
this link
</xsl:for-each>
</p>
</xsl:for-each>
produces
<p>blah blah blah</p>
<p>blah blah, refer to for further details.this link</p>
What I want is
<p>blah blah blah</p>
<p>blah blah, refer to this link for further details.</p>
Any ideas? No, I don't have control over the content of the XML.
Use just simple overriding of the identity rule:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="linkCode">
this link
</xsl:template>
<xsl:template match="parag">
<p><xsl:apply-templates select="node()|#*"/></p>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document (the provided fragment wrapped into a single top element -- to get a well-formed XML document):
<t>
<parag>blah blah blah</parag>
<parag>blah blah, refer to <linkCode code1="a" code2="b" code3="c"/> for further details.</parag>
</t>
the wanted, correct result is produced:
<t>
<p>blah blah blah</p>
<p>blah blah, refer to this link for further details.</p>
</t>
And if you want to have the top element not output:
just add this template:
<xsl:template match="/*"><xsl:apply-templates/></xsl:template>
So, the complete code becomes:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="linkCode">
this link
</xsl:template>
<xsl:template match="parag">
<p><xsl:apply-templates select="node()|#*"/></p>
</xsl:template>
<xsl:template match="/*"><xsl:apply-templates/></xsl:template>
</xsl:stylesheet>

Is it possible to group by full-stop in this XML?

I'd like to impose some structure on the following XML:
<phr>ΦP
<i>το αθάνατο</i> ~.
<i>ήπιε το αμίλητο</i> ~.
<i>πίνω</i> ~ <i>στο όνομα κάποιου</i>, τον σέβομαι, τον εκτιμώ, αναγνωρίζοντας τα όσα του οφείλω.
<i>λέω το</i> ~ <i>νεράκι</i>, για μεγάλη έλλειψη νερού.
</phr>
by grouping the contents of with the full-stop as a separator, as in:
<phr>
<group>ΦP <i>το αθάνατο</i> ~.</group>
<group> <i>ήπιε το αμίλητο</i> ~.</group>
<group> <i>πίνω</i> ~ <i>στο όνομα κάποιου</i>, τον σέβομαι, τον εκτιμώ, αναγνωρίζοντας τα όσα <group>του οφείλω.</group>
<group><i>λέω το</i> ~ <i>νεράκι</i>, για μεγάλη έλλειψη νερού.</group>
</phr>
Can anyone recommend some XSLT (2.0) for achieving this? I've tried for-each-group group-by 'text()[matches(.,'.')]', but that doesn't yield the desired result.
Many thanks!
This stylesheet should do what you need:
<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">
<!-- Identity template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="phr">
<xsl:copy>
<xsl:for-each-group select="node()" group-ending-with="text()[contains(.,'.')]">
<group>
<xsl:apply-templates select="current-group()"/>
</group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Resources