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>
Related
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
<a>
<z/>
<b/>
<b/>
<b/>
<c/>
</a>
I want to find the count of 'b' within 'a' when my parsing current node is 'c' using XSLT.
Is it possible to do this using XSLT?
I am not aware of what the element name 'b' would be, i.e. for its preceding sibling.
If you are positioned on c tag, or whatever the element is actually called, then to get the count of the preceding siblings, you would do this...
<xsl:value-of select="count(preceding-sibling::*)" />
EDIT: In answer to your comment, if you don't want to count all siblings, but only the count of the immediately preceding one, and the ones with the same name before that, you could try this...
<xsl:value-of select="count(preceding-sibling::*[name() = name(current()/preceding-sibling::*[1])])" />
This would not work though in the case you had multiple c nodes under one parent...
<a>
<z/>
<b/>
<b/>
<b/>
<c/>
<z/>
<b/>
<c/>
</a>
In this case, you could define a key like this, to group elements by the unique id of the first following element with a different name:
<xsl:key name="keyc" match="*" use="generate-id(following-sibling::*[name() != name(current())][1])" />
Then you can get the count like so:
<xsl:value-of select="count(key('keyc', generate-id()))" />
Here are the three options in action....
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="keyc" match="*" use="generate-id(following-sibling::*[name() != name(current())][1])" />
<xsl:template match="c">
<c>
<example1>
<xsl:value-of select="count(preceding-sibling::*)" />
</example1>
<example2>
<xsl:value-of select="count(preceding-sibling::*[name() = name(current()/preceding-sibling::*[1])])" />
</example2>
<example3>
<xsl:value-of select="count(key('keyc', generate-id()))" />
</example3>
</c>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Use xsl:number. It prints out the numer of the current element, formatted as required.
There are various options concerning how to perform the numeration,
e.g. multi-level or alphabetic one.
Actually it is quite a powerful tool.
I want to find the count of 'b' within 'a' when my parsing current node is 'c'
Let me rephrase that:
you want to count all occurrences of <b> which are on the same level as <c>.
This XSLT does the job by calling an <xsl:template> with a parameter:
the local-name of the element to be counted (in this case 'b'):
<?xml version="1.0"?>
<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="//c"> <!-- select the desired target element -->
<xsl:call-template name="count">
<xsl:with-param name="elemName" select="'b'" /> <!-- name of the element -->
</xsl:call-template>
</xsl:template>
<xsl:template name="count"> <!-- count all matching elements before and after -->
<xsl:param name="elemName" />
<xsl:value-of select="count(preceding-sibling::*[local-name() = $elemName]) + count(following-sibling::*[local-name() = $elemName])" />
</xsl:template>
</xsl:stylesheet>
In the case of your example the output simply is:
3
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.
In my source XML, any element can have an #n attribute. If one does, I want to output it before processing the element and all its children.
For example
<line n="2">Ipsum lorem</line>
<verse n="5">The sounds of silence</verse>
<verse>Four score and seven</verse>
<sentence n="3">
<word n="1">Hello</word>
<word n="2">world</word>
</sentence>
I have templates that match "line", "verse", "sentence" and "word". If any of those elements has an #n value, I want to output it in front of whatever the element's template generates.
The above might come out something like
2 <div class="line">Ipsum lorem</span>
5 <span class="verse">The sounds of silence</span>
<span class="verse">Four score and seven</span>
3 <p class="sentence">
1 <span class="word">Hello</span>
2 <span class="word">world</span>
</p>
where the templates for "line", "verse", etc. generated the div, span and p elements.
How should I think of this problem? -- Match the attribute and then apply-templates to its parent? (What would the syntax for that be?) Put a call-template at the beginning of every element's template? (That's unappealing.) Something else? (Probably!)
I tried a few things but got either an infinite loop, or nothing, or processing of the attribute and then its parent's children, but not the parent itself.
To simplify matters, I've placed the mapping from XML to HTML elements in an in-document data structure (accessible via the document() function with no arguments). Now only one template is needed requiring special processing of the #n attribute in only one place.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<map>
<elt xml="line" html="class"/>
<elt xml="verse" html="span"/>
<elt xml="sentence" html="p"/>
<elt xml="word" html="span"/>
</map>
<xsl:template match="line|verse|sentence|word">
<xsl:if test="#n"><xsl:value-of select="#n"/> </xsl:if>
<xsl:element name="{document()/map/elt[#xml=name()]/#html}">
<xsl:attribute name="class"><xsl:value-of select="name()"/></xsl:attibute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
Here is one simple way to do this:
<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="*/*[#n]">
<xsl:value-of select="concat('
', #n, ' ')"/>
<xsl:apply-templates select="self::*" mode="content"/>
</xsl:template>
<xsl:template match="*/*[not(#*)]">
<xsl:apply-templates select="." mode="content"/>
</xsl:template>
<xsl:template match="line" mode="content">
<div class="line"><xsl:apply-templates/></div>
</xsl:template>
<xsl:template match="verse | word" mode="content">
<span class="{name()}"><xsl:apply-templates mode="content"/></span>
</xsl:template>
<xsl:template match="sentence" mode="content">
<p class="sentence"><xsl:apply-templates/></p>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<t>
<line n="2">Ipsum lorem</line>
<verse n="5">The sounds of silence</verse>
<verse>Four score and seven</verse>
<sentence n="3">
<word n="1">Hello</word>
<word n="2">world</word>
</sentence>
</t>
the wanted, correct result is produced:
2 <div class="line">Ipsum lorem</div>
5 <span class="verse">The sounds of silence</span>
<span class="verse">Four score and seven</span>
3 <p class="sentence">
1 <span class="word">Hello</span>
2 <span class="word">world</span>
</p>
Explanation: Appropriate use of templates and modes.
I have the following XML:
<Info>
<Name>Dan</Name>
<Age>24</Age>
</Info>
<Info>
<Name>Tom</Name>
<Age>15</Age>
</Info>
<Info>
<Name>Dan</Name>
<Age>24</Age>
</Info>
<Info>
<Name>James</Name>
<Age>18</Age>
</Info>
And I need to produce the following HTML:
<ul class="data">
<li>Dan</li>
<li>Dan</li>
</ul>
<ul class="data">
<li>James</li>
</ul>
<ul class="data">
<li>Tom</li>
<li>Tom</li>
</ul>
As well as producing the output it needs to sort based on the Name also. Any help appreciated, started by looking at group-by but couldnt work out how to get it finished:
Pretty sure its wrong?
<xsl:for-each-group select="Info" group-by="#Name">??????
<xsl:for-each-group select="current-group()" sort-by="#Name">
I don't have an XSLT 2.0 parser, but to do it in XSLT 1.0 at least you could use Muenchian Grouping to do this...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="html" version="1.0"/>
<xsl:key name="Names" match="Name" use="."/>
<xsl:template match="/">
<xsl:for-each select="//Info[generate-id(Name) = generate-id(key('Names', Name)[1])]">
<xsl:sort select="Name" />
<ul class="data">
<xsl:for-each select="key('Names', Name)">
<li><xsl:value-of select="." /></li>
</xsl:for-each>
</ul>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I'm not sure why your result has two 'Tom' elements, I assume you have an extra node in the XML that you didn't provide in your sample.
Anyway, the XSLT would look something like this:
<xsl:for-each-group select="Info" group-by="Name">
<xsl:sort select="current-grouping-key()"/>
<ul class="data">
<xsl:for-each select="current-group()/Name">
<li><xsl:value-of select="." /></li>
</xsl:for-each>
</ul>
</xsl:for-each-group>
I don't have an XSLT 2.0 parser handy to test it, but I think that should work.
I don't have an xslt 2.0 parser to test this, but at the very least you will need to change "#Name" to just "Name", since it is a subelement not an attribute.