XSLT insert at right location - xslt

My input XML is as follows. Basically the XML has various <servlet> tags. My requirement is to apply a XSLT transform which browses through <servlet-name> tags and see if a particular servlet with specified name exists. If it exists then i need to see that tag <B> under that particular servlet with a <param-name>does not EXIST. If the tag with a specific <param-name> under the searched doesn't exist then i add the tag <B>NEW</B> along with other <B> tags of that particular servlet else i do not perform any action.
INPUT XML
<web-app metadata-complete="true">
<servlet>
<servlet-name>AAA</servlet-name>
<servlet-class>com.AAA</servlet-class>
<B>
<param-name>port</param-name>
<param-value>8802</param-value>
</B>
<B>
<param-name>connectors-xml</param-name>
<param-value/>
</B>
<B>
<param-name>webservices-xml</param-name>
<param-value/>
</B>
<B>
<param-name>exposure-server</param-name>
<param-value/>
</B>
<some-tag>1</some-tag>
</servlet>
<servlet>
<servlet-name>BBB</servlet-name>
<servlet-class>com.BBB</servlet-class>
<B>
<param-name>port</param-name>
<param-value>8802</param-value>
</B>
<B>
<param-name>connectors-xml</param-name>
<param-value/>
</B>
<B>
<param-name>webservices-xml</param-name>
<param-value/>
</B>
<B>
<param-name>exposure-server</param-name>
<param-value/>
</B>
<some-tag>2</some-tag>
</servlet>
<C>
<D>
</D
</C>
<junk-tag>
<tag1>BASIC</tag1>
<tag2>BASIC</tag2>
</junk-tag>
</web-app>
eg. Lets say i search for a Servlet with Servlet name as "BBB" below. If found, then i check that its <B> tag with <param-name> value XXX doesn't exist then i add it so that o/p looks as below. If "BBB" <servlet-name> has <B> tag with <param-name> value XXX already present then i do not do anything.
OUTPUT.XML
<web-app metadata-complete="true">
<servlet>
<servlet-name>AAA</servlet-name>
<servlet-class>com.AAA</servlet-class>
<B>
<param-name>port</param-name>
<param-value>8802</param-value>
</B>
<B>
<param-name>connectors-xml</param-name>
<param-value/>
</B>
<B>
<param-name>webservices-xml</param-name>
<param-value/>
</B>
<B>
<param-name>exposure-server</param-name>
<param-value/>
</B>
<some-tag>1</some-tag>
</servlet>
<servlet>
<servlet-name>BBB</servlet-name>
<servlet-class>com.BBB</servlet-class>
<B>
<param-name>port</param-name>
<param-value>8802</param-value>
</B>
<B>
<param-name>connectors-xml</param-name>
<param-value/>
</B>
<B>
<param-name>webservices-xml</param-name>
<param-value/>
</B>
<B>
<param-name>exposure-server</param-name>
<param-value/>
</B>
<B>NEW</B>
<some-tag>2</some-tag>
</servlet>
<C>
<D>
</D
</C>
<junk-tag>
<tag1>BASIC</tag1>
<tag2>BASIC</tag2>
</junk-tag>
</web-app>
I have tried writing a XSLT but somehow caught in BUGS and syntax issues
<xsl:template match="web-app/servlet[servlet-name='BBB/B']">
<xsl:copy-of select="."/>
<xsl:choose>
<xsl:when test="not(/web-app/servlet[servlet-name='BBB']/B[param-name='XXX'])">
<B>NEW</B>
</xsl:when>
</xsl:choose>
</xsl:template>
Any guidance? I am NOVICE to XSLT and attempting by googling itself.

The expression you want is just not(B[param-name='XXX']) as you are already positioned on the relevant servlet at the point, so this expression will be relevant to that. Additionally your current code will copy the existing servlet and add <B>NEW</B> after it, when in fact you want it as a child.
So you could do this....
<xsl:template match="web-app/servlet[servlet-name='BBB']">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:if test="not(B[param-name='XXX'])">
<B>NEW</B>
</xsl:if>
</xsl:copy>
</xsl:template>
Or better still, put the check in the template match itself
<xsl:template match="web-app/servlet[servlet-name='BBB'][not(B[param-name='XXX'])]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<B>NEW</B>
</xsl:copy>
</xsl:template>
(Both of these assume you are also using the identity template)
However, this adds your new tag after <some-tag>2</some-tag> which may not be what you want.
If you want to place it after the last B element, you should change the template to match the last B element instead.
<xsl:template match="web-app/servlet[servlet-name='BBB'][not(B[param-name='XXX'])]/B[last()]">
<xsl:copy-of select="." />
<B>NEW</B>
</xsl:template>
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="web-app/servlet[servlet-name='BBB'][not(B[param-name='XXX'])]/B[last()]">
<xsl:copy-of select="." />
<B>NEW</B>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT: Copy two files into one common structure

I try to merge result of SSIS Data Profiler Task for several tables into one XML for inspection of the results within one single file inside "Data Profiler Viewer". The whole problem shrinks to the stronly simplified XML-trasformation here:
File 1 (test_1.xml):
<a xmlns="http://schemas.microsoft.com/sqlserver/2008/DataDebugger/">
<b id="1"/>
<c>
<2: any other XML-structure to come here/>
</c>
</a>
File 2 (test_2.xml):
<a xmlns="http://schemas.microsoft.com/sqlserver/2008/DataDebugger/">
<b id="1"/>
<c>
<1: any other XML-structure to come here/>
</c>
</a>
(Element b is always exacly the same)
Expected result:
<a xmlns="http://schemas.microsoft.com/sqlserver/2008/DataDebugger/">
<b id="1"/>
<c>
<1: any other XML-structure to come here/>
<2: any other XML-structure to come here/>
</c>
</a>
Any help is stronly recommended! I will provide the solution to the original problem here.
Another try:
<?xml version='1.0' encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://schemas.microsoft.com/sqlserver/2008/DataDebugger/"
version="1.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" version="1.0" encoding="UTF-8"/>
<xsl:template match="t:c">
<xsl:element name="c" namespace="http://schemas.microsoft.com/sqlserver/2008/DataDebugger/">
<xsl:copy-of select="*" />
<xsl:copy-of select="document('test_2.xml')//t:c/node() " />
</xsl:element>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
checked with xalan (set classpath in environment)
java org.apache.xalan.xslt.Process -IN test1_1.xml -XSL test1.xslt -OUT test1_12.xml
and saxon (Change skript to Version = "1.1")
java -jar saxon-9.1.0.8j.jar -s:test_1.xml -xsl:test_1.xslt -o:test_12.xml

Checking multiple occurrences and manipulating them using XSLT

I have a requirement in which I need to check all four values of both occurrences in one condition and it is not given that hey1 and hello1 will come in one occurrence and hey2 and hello2 in the other.
I mean it is not guaranteed that hey1 or hey2 come in as first or second; the same applies for hello1 and hello2.
I am using the below code which gives me the output <output>ININ</output>, but I need it as <output>IN</output>.
I am trying this POC and I have a sample XML below:
<q>
<a>
<b>hey1</b>
<c>hello1</c>
</a>
<a>
<b>hey2</b>
<c>hello2</c>
</a>
</q>
Kindly provide me a solution which checks all four conditions and in which one if/when condition used for each <a> will occur twice or multiple times.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output encoding="UTF-8" version="1.0" method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="a" select="/q/a"/>
<output>
<xsl:for-each select="$a">
<xsl:if test="$a/b='hey1' and $a/c= 'hello1' and $a/b='hey2' and $a/c= 'hello2'">
<xsl:value-of select="'IN'"/>
</xsl:if>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
I hope that I my question is clear enough to answer it.
Thank you for any advice.
Try:
<xsl:template match="q">
<xsl:if test="a[b='hey1' and c='hello1'] and a[b='hey2' and c='hello2'] ">
<xsl:text>IN</xsl:text>
</xsl:if>
</xsl:template>
This will return "IN" for both:
<q>
<a>
<b>hey1</b>
<c>hello1</c>
</a>
<a>
<b>hey2</b>
<c>hello2</c>
</a>
</q>
and:
<q>
<a>
<b>hey2</b>
<c>hello2</c>
</a>
<a>
<b>hey1</b>
<c>hello1</c>
</a>
</q>
but not for:
<q>
<a>
<b>hey1</b>
<c>hello2</c>
</a>
<a>
<b>hey2</b>
<c>hello1</c>
</a>
</q>

How to clear element value in xslt if the value is a string

I just wanna clear a element value if element value is a specific string value
Input xml
<A>
<B>
<C>BOLD</C>
</B>
</A>
Desired Output
<A>
<B>
<C/>
</B>
</A>
my xslt looks like following which doesn't work it just clears everything
<xsl:template match="A/B/C/text()">
<xsl:if test="text()='BOLD'">
<xsl:text></xsl:text>
</xsl:if>
</xsl:template>
<!--Copy the rest of the document as it is-->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
Please help thanks
Given you've started with an identity template there's really no need to use an explicit xsl:if, just put the condition in the match expression of an empty template:
<xsl:template match="A/B/C/text()[. = 'BOLD']"/>
Text nodes that are not under an A/B/C or whose content is not BOLD will be handled by the identity template and copied as normal.
The caveat with using text() in your match expressions is that it means the template is looking at only one text node at a time, and only at text that is directly inside the C element. If the C can have other content you will get some odd edge cases:
<C>BOLD<br/></C> --> <C><br/></C>
<C><b>B</b>OLD</C> --> unchanged
<C><b>BOLD</b></C> --> unchanged
If you want to treat the full text under the C as one unit regardless of child elements then you need something more like
<xsl:template match="C[. = 'BOLD']">
<C/>
</xsl:template>
without mentioning text(). This would clear all three of my examples above.
The logic in your first template isn't outputting anything if the text isn't "BOLD", so if the text is bold, its spits out nothing, otherwise it still spits out nothing. also, the text() node won't have its own text.
So, replace the first template it with this, and it should work fine.
<xsl:template match="A/B/C/text()">
<xsl:if test="not(.='BOLD')">
<xsl:copy/>
</xsl:if>
</xsl:template>
And applied to this:
<A>
<B>
<C>Bold</C>
<C>BOLD</C>
<C>Italic</C>
</B>
</A>
Gives this:
<A>
<B>
<C>Bold</C>
<C/>
<C>Italic</C>
</B>
</A>
Another way to fix this issue is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[text()='BOLD']">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>

XSL: Comparing nodes by comparing their child nodes

I would like to be able to compare two nodes based on the values of their child nodes. Testing node equality with the = operator just compares the string values of the respective nodes. I would like to compare them based on values in their child nodes.
To be a bit more specific, I would like <a> and <b> (below) to be equal, because the values of #id are the same for <c> elements that have matching #type attributes also have matching #id attributes.
<a>
<c type="type-one" id="5675"/>
<c type="type-two" id="3423"/>
<c type="type-three" id="9088"/>
</a>
<b>
<c type="type-one" id="5675"/>
<c type="type-two" id="3423"/>
</b>
But these would be different:
<a>
<c type="type-one" id="5675"/>
</a>
<b>
<c type="type-one" id="2342"/>
</b>
The only solution I can begin to see involves a laborious comparison with a for-each statement, which I would like to avoid if possible.
If possible I would like to stick with XSLT 1.0. I am using xsltproc.
First of all, the relation called "equals" cannot have that name.
"Equals" means that the relation is an equivalence relation. By definition any equivalence relation ~ must be:
Reflexive: x ~ x .
Symmetric: if x ~ y then y ~ x
Transitive: if x ~ y and y ~ z then x ~ z .
Here is an example, showing that the proposed "equals" relation isn't transitive:
x is:
<a>
<c type="type-one" id="5675"/>
<c type="type-two" id="3423"/>
<c type="type-three" id="9088"/>
</a>
y is:
<b>
<c type="type-one" id="5675"/>
<c type="type-two" id="3423"/>
<c type="type-four" id="1234"/>
</b>
z is:
<b>
<c type="type-three" id="3333"/>
<c type="type-four" id="1234"/>
</b>
Now, we can see that x ~ y and y ~ z. However, clearly this doesn't hold: x ~ z
This said, I am calling the relation "matches" and it is relaxed and not "equals".
Here is a solution to the problem, with the above adjustment:
Do note that this cannot be expressed with a single XPath expression, because XPath 1.0 (used within an XSLT 1.0 transformation) doesn't have range variables.
<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="/*">
<xsl:call-template name="matches">
<xsl:with-param name="pElem1" select="a"/>
<xsl:with-param name="pElem2" select="b"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="matches">
<xsl:param name="pElem1" select="/.."/>
<xsl:param name="pElem2" select="/.."/>
<xsl:variable name="vMisMatch">
<xsl:for-each select="$pElem1/c[#type = $pElem2/c/#type]">
<xsl:if test=
"$pElem2/c[#type = current()/#type and not(#id = current()/#id)]">1</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:copy-of select="not(string($vMisMatch))"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<t>
<a>
<c type="type-one" id="5675"/>
<c type="type-two" id="3423"/>
<c type="type-three" id="9088"/>
</a>
<b>
<c type="type-one" id="5675"/>
<c type="type-two" id="3423"/>
</b>
</t>
the wanted, correct result is produced:
true
When the same transformation is applied on this XML document:
<t>
<a>
<c type="type-one" id="5675"/>
<c type="type-two" id="3423"/>
<c type="type-three" id="9088"/>
</a>
<b>
<c type="type-one" id="5675"/>
<c type="type-two" id="9876"/>
</b>
</t>
again the correct result is produced:
false
Here is what I came up with. Given a toy data set like this:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>
<item key="x" value="123"/>
<item key="y" value="456"/>
<item key="z" value="789"/>
</a>
<b>
<item key="x" value="123"/>
<item key="z" value="789"/>
</b>
</root>
This stylesheet shows how to test equality, as defined in the question.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:set="http://exslt.org/sets" xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="set exsl">
<xsl:output method="text" version="1.0" encoding="UTF-8"/>
<xsl:template match="/">
<xsl:variable name="values-are-equal">
<xsl:call-template name="equal">
<xsl:with-param name="A" select="/root/a"/>
<xsl:with-param name="B" select="/root/b"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$values-are-equal = 1">Equal</xsl:when>
<xsl:otherwise>Inequal</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="equal">
<xsl:param name="A" />
<xsl:param name="B" />
<xsl:variable name="common-keys" select="$A/item/#key[ count(set:distinct( . | $B/item/#key )) = count( set:distinct( $B/item/#key ) ) ]"/>
<xsl:variable name="differences">
<xsl:for-each select="$common-keys">
<xsl:if test="$A/item[#key = current()]/#value != $B/item[#key = current()]/#value">
<different/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:choose>
<xsl:when test="count( exsl:node-set($differences)/* ) > 0">0</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This uses some extensions, which are available in xsltproc and other processors.

Creating reusable template in xslt to process some elements which are occuring multiple times

I want to write a template to process some section and reuse it.
In the below XML, we see Message is repeated in every ,,. I want to write a template to process and call it when ever needed.
<Data>
<A>
<text>this is text</text>
<Message>
<local>Local link</local>
<STD>External link</STD>
</Message>
</A>
<B>
<info>Information</info>
<Message>
<local>Local uri link</local>
<STD>External link uri</STD>
</Message>
</B>
<C>
<longtext>Long Text</longtext>
<Message>
<local>Local uri link</local>
<STD>External link uri</STD>
</Message>
</C>
<Data>
Output needed:
<Information>
<AA>
this is text
<MSG local value="Local uri link" STD value="External link"/>
</AA>
<BB>
Information
<MSG local value ="Local uri link" STD value="External link"/>
</BB>
<CC>
Long Text
<MSG local value="Local uri link" STD value="External link"/>
</CC>
<Information>
While processing tag in every node, I am writing code for every tags in A, B, C.
Sample code written
<Information>
<xsl:template match="A">
<AA>
<xsl:value-of select="text"/>
<xsl:element name="MSG">
<xsl:attribute name="local value">
<xsl:value-of select="Message/local"/>
</xsl:attribute>
<xsl:attribute name="STD value">
<xsl:value-of select="Message/STD"/>
</xsl:attribute>
</AA>
</Information>
similarly for every template I am explicitly writing the code for the block MSG.
Now I want to write a separate template to process . And I want to call this template from every template.
Basically I want to reuse the code written to process
Can any one help me how to do it.
Thank you.
Here is a sample:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Data">
<Information>
<xsl:apply-templates/>
</Information>
</xsl:template>
<xsl:template match="Data/*">
<xsl:element name="{local-name()}{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="Data/A/text | Data/B/info | Data/C/longtext">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="Message">
<MSG local-value="{local}" STD-value="{STD}"/>
</xsl:template>
</xsl:stylesheet>
When applied to the input
<Data>
<A>
<text>this is text</text>
<Message>
<local>Local link</local>
<STD>External link</STD>
</Message>
</A>
<B>
<info>Information</info>
<Message>
<local>Local uri link</local>
<STD>External link uri</STD>
</Message>
</B>
<C>
<longtext>Long Text</longtext>
<Message>
<local>Local uri link</local>
<STD>External link uri</STD>
</Message>
</C>
</Data>
Saxon 6.5.5 outputs
<?xml version="1.0" encoding="utf-8"?><Information>
<AA>
this is text
<MSG local-value="Local link" STD-value="External link"/>
</AA>
<BB>
Information
<MSG local-value="Local uri link" STD-value="External link uri"/>
</BB>
<CC>
Long Text
<MSG local-value="Local uri link" STD-value="External link uri"/>
</CC>
</Information>
Neither your posted input sample nor the wanted output sample are well-formed XML so I had to make some changes to element or attribute names to process as well as to output XML.