i am converting docx file into html using xslt. My resulting html contains styles like margin-top:NaN pt;, the style value NaN is ignored in browser by default.But i have to validate for the presence of such attributes and have to remove before viewing in browser...
Please help me...Thanks in advance...
Have you tried W3C CSS validator?
http://jigsaw.w3.org/css-validator/
You can use it programatically thanks to a SOAP Web service:
http://jigsaw.w3.org/css-validator/api.html
You should check for 'NaN' before adding inline styles.
E.g. consider this XML:
<?xml version="1.0"?>
<t>
<Number>adsfdasf</Number>
<Number></Number>
<Number>100</Number>
<Number>1.234234</Number>
</t>
Then you can:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Number">
<xsl:value-of select="concat(., ' : ')"/>
<xsl:if test="not(string(number()) = 'NaN')">valid</xsl:if>
<xsl:if test="string(number()) = 'NaN'">invalid</xsl:if>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Result:
adsfdasf : invalid
: invalid
100 : valid
1.234234 : valid
It is too-late to chack for NaN in the generated result.
Producing unwanted output should be prevented!
Here is an example, that avoids generating NaN:
<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="num[number(.) = number(.)]">
<span style="margin-top:{.}"/>
</xsl:template>
<xsl:template match="num[not(number(.) = number(.))]"/>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<t>
<num>helo</num>
<num></num>
<num>100</num>
<num>1.234234</num>
</t>
only correct output (no NaN) is produced:
<t>
<span style="margin-top:100"/>
<span style="margin-top:1.234234"/>
</t>
Related
I have a problem. I think it is simple but I havent found it.
like below code I want to find and match multiplier factor value like "0.05" with "005" in the note value dynamically and take text "AAA" after "_ 005 _"(without spaces) and write it and others too.
I tried to use together both format number and concatenate to "concat(format-number(***))" but failed because of newby about this.
<cbc:Note>40.00 BT</cbc:Note>
<cbc:Note>17_ 2005.00</cbc:Note>
<cbc:Note>11_005_AAA</cbc:Note>
<cbc:Note>11_002_BBB</cbc:Note>
<cbc:Note>11_003_CCC</cbc:Note>
<cbc:InvoicedQuantity unitCode="CS">1.000</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="TRY">200.00</cbc:LineExtensionAmount>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:MultiplierFactorNumeric>0.03</cbc:MultiplierFactorNumeric>
<cbc:Amount currencyID="TRY">6.00</cbc:Amount>
<cbc:BaseAmount currencyID="TRY">200.00</cbc:BaseAmount>
</cac:AllowanceCharge>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:MultiplierFactorNumeric>0.05</cbc:MultiplierFactorNumeric>
<cbc:Amount currencyID="TRY">10.00</cbc:Amount>
<cbc:BaseAmount currencyID="TRY">200.00</cbc:BaseAmount>
</cac:AllowanceCharge>
current xslt block
<xsl:for-each select="./cac:AllowanceCharge/cbc:MultiplierFactorNumeric">
<br/>
<xsl:text> %</xsl:text>
<xsl:value-of select="format-number(. * 100, '###.##0,00', 'european')"/>,
</xsl:for-each>
I appreciate if you help me.
It is very difficult to understand your question.
The following minimal example is mostly based on a guess. It formats the MultiplierFactorNumeric value as a 3-digit whole number and uses it as a key to retrieve the corresponding Note value:
XML
<root>
<Note>40.00 BT</Note>
<Note>17_ 2005.00</Note>
<Note>11_005_AAA</Note>
<Note>11_002_BBB</Note>
<Note>11_003_CCC</Note>
<AllowanceCharge>
<MultiplierFactorNumeric>0.03</MultiplierFactorNumeric>
</AllowanceCharge>
<AllowanceCharge>
<MultiplierFactorNumeric>0.05</MultiplierFactorNumeric>
</AllowanceCharge>
</root>
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:key name="note" match="Note" use="substring-before(substring-after(., '_'), '_')" />
<xsl:template match="/root">
<result>
<xsl:for-each select="AllowanceCharge">
<item>
<factor>
<xsl:value-of select="MultiplierFactorNumeric"/>
</factor>
<value>
<xsl:value-of select="substring-after(substring-after(key('note', format-number(100*MultiplierFactorNumeric, '000')), '_'), '_')"/>
</value>
</item>
</xsl:for-each>
</result>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<result>
<item>
<factor>0.03</factor>
<value>CCC</value>
</item>
<item>
<factor>0.05</factor>
<value>AAA</value>
</item>
</result>
i would like to find the highest temperate of the month APRIL, the xml has other month too.
how can i code in my xsl to retrieve the highest temperate?
here is my XML code
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="THISISA3.xsl"?>
<forecast qTime="28/10/20 10:00 PM" qLocation="Singapore">
<weather yyyymmdd="20200430">
<year>2020</year>
<month>04</month>
<date>30</date>
<comment>Plenty of sunshine</comment>
<code>sunny</code>
<highest>32.6</highest>
<lowest>28.4</lowest>
</weather>
<weather yyyymmdd="20200421">
<year>2020</year>
<month>04</month>
<date>21</date>
<comment>Plenty of sunshine</comment>
<code>sunny</code>
<highest>32.2</highest>
<lowest>29.8</lowest>
</weather>
</forecast>
It is good to provide the required output and also to show the efforts made to get the result as the community here to help with specific problems and not to write the whole code.
However, if you're newbie to start with xslt then below code with comments could help you to get started and to understand the use of stuff in xslt.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- key is used to identify nodes within an expression -->
<xsl:key name="highTemp" match="highest" use="."/>
<!-- identity template to result the transformation in source XML doc itself -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- matching a root node -->
<xsl:template match="forecast">
<!-- iterating through each 'weather' element -->
<xsl:for-each select="weather">
<!-- selecting node which matches the variable having required highest temperature -->
<xsl:apply-templates select=".[highest=$monthApril]"/>
</xsl:for-each>
</xsl:template>
<xsl:variable name="monthApril">
<!-- iterate only 'weather' elements which are from month of April -->
<xsl:for-each select="//weather[substring(#yyyymmdd,5,2) = '04']">
<!-- sorting in descending order so that node having highest temp comes first -->
<xsl:sort select="key('highTemp', highest)" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="highest"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
</xsl:stylesheet>
It would be nice if you specified what the required output is and how the variables are set and which version of xslt you use.
Below is an example XSLT 1.0 template (it has name 'highestTempForMonth') with the ability to specify the required month (it can be specified in a parameter 'month' for template) and with the ability to call this template with required month in any place you want to do it.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:output method="xml" indent="yes" encoding="Utf-8"/>
<xsl:variable name="requiredMonth" select="'04'"/>
<xsl:template match="/">
<HighestTemperature>
<value>
<xsl:call-template name="highestTempForMonth">
<xsl:with-param name="month" select="$requiredMonth"/>
</xsl:call-template>
</value>
</HighestTemperature>
</xsl:template>
<xsl:template name="highestTempForMonth">
<xsl:param name="month"/>
<xsl:for-each select="/forecast/weather[month = $month]/highest">
<xsl:sort select="." data-type="number" order="descending"/>
<xsl:if test="position() = 1"><xsl:value-of select="."/></xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
There is XSLT 2.0 equivalent (with a little bit simpler template part) example.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" version="2.0" >
<xsl:output method="xml" indent="yes" encoding="Utf-8"/>
<xsl:variable name="requiredMonth" select="'04'"/>
<xsl:template match="/">
<HighestTemperature>
<value>
<xsl:call-template name="highestTempForMonth">
<xsl:with-param name="month" select="$requiredMonth"/>
</xsl:call-template>
</value>
</HighestTemperature>
</xsl:template>
<xsl:template name="highestTempForMonth">
<xsl:param name="month"/>
<xsl:value-of select="max(/forecast/weather[month = $month]/highest/xs:double(.))"/>
</xsl:template>
</xsl:stylesheet>
For both examples if there are no values for the specified month, the 'value' tag with an empty value will be specified. This value can be changed by a simple condition if necessary.
I'm trying to convert the following XML document:
<method>
<desc_signature>
<desc_name>
Some method name
</desc_name>
</desc_signature>
<desc_content>
<paragraph>Paragraph1</paragraph>
<image>Image1</image>
<paragraph>Paragraph2</paragraph>
<literal_block>Codesnippet1</literal_block>
<image>Image2</image>
<paragraph>Paragraph3</paragraph>
<image>Image3</image>
<literal_block>Codesnippet2</literal_block>
</desc_content>
</method>
To the following JSON format:
{
"title":"Some method name",
"elements":[
"Paragraph1",
"Paragraph2",
"Codesnippet1",
"Paragraph3",
"Codesnippet2",
]
}
What I basically want to achieve, is to comma separate a list, but only include paragraph and literal_blocks, and still keeping the original order.
My first attempt was an XSLT that looks like this, with this union selector:
<xsl:for-each select="desc_content/paragraph|desc_content/literal_block">
Like this:
<!-- Method template -->
<xsl:template name="method">
{
"title":"<xsl:value-of select="normalize-space(desc_signature/desc_name)" />",
"elements":[
<xsl:for-each select="desc_content/paragraph|desc_content/literal_block">
<xsl:choose>
<xsl:when test="self::paragraph">
<xsl:call-template name="paragraph"/>
</xsl:when>
<xsl:when test="self::literal_block">
<xsl:call-template name="code"/>
</xsl:when>
</xsl:choose>
<xsl:if test="position()!=last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
]
}
</xsl:template>
However, it seems like it is grouping paragraphs and then literal_blocks:
{
"title":"Some method name",
"elements":[
"Paragraph1",
"Paragraph2",
"Paragraph3",
"Codesnippet1",
"Codesnippet2",
]
}
Another approach was to select all:
<xsl:for-each select="desc_content/*">
That, however, yields too many commas, as position() accounts for all elements, even if not used:
{
"title":"Some method name",
"elements":[
"Paragraph1",,
"Paragraph2",
"Codesnippet1",,
"Paragraph3",,
"Codesnippet2",
]
}
How would I be able to achieve the desired behaviour?
Thanks for the help!
EDIT - XSLT 1.0 solution:
I've solved the issue with the solution from #Christian Mosz, using apply-templates and checking following-sibling:
<!-- Method template -->
<xsl:template name="method">
{
"title":"<xsl:value-of select="normalize-space(desc_signature/desc_name)" />",
"elements":[
<xsl:apply-templates select="desc_content/*"/>
]
}
</xsl:template>
<!-- Paragraph template -->
<xsl:template match="paragraph">
{"paragraph":"<xsl:value-of select="normalize-space()"/>"}
<xsl:if test="following-sibling::paragraph|following-sibling::literal_block">,</xsl:if>
</xsl:template>
<!-- Code snippet template -->
<xsl:template match="literal_block">
{"code":"<xsl:call-template name="code"/>"}
<xsl:if test="following-sibling::paragraph|following-sibling::literal_block">,</xsl:if>
</xsl:template>
For XSLT 3.0, please check approved answer.
Using XSLT 3 (e.g. with Saxon 9.8 or later or AltovaXML 2017 R3 or later) you have two options, you can either construct an XPath 3.1 map:
map {
'title' : normalize-space(desc_signature/desc_name),
'elements' : array {
data(desc_content/(paragraph | literal_block))
}
}
i.e.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="json" indent="yes" />
<xsl:template match="method">
<xsl:sequence
select="map {
'title' : normalize-space(desc_signature/desc_name),
'elements' : array {
data(desc_content/(paragraph | literal_block))
}
}"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/pNmC4HJ
or you transform to the XML representation of JSON the xml-to-json functions supports:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.w3.org/2005/xpath-functions"
expand-text="yes"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="text" indent="yes" />
<xsl:template match="/">
<xsl:variable name="json-xml">
<xsl:apply-templates/>
</xsl:variable>
<xsl:value-of select="xml-to-json($json-xml, map { 'indent' : true() })"/>
</xsl:template>
<xsl:template match="method">
<map>
<string key="title">{normalize-space(desc_signature/desc_name)}</string>
<array key="elements">
<xsl:apply-templates select="desc_content/(paragraph | literal_block)"/>
</array>
</map>
</xsl:template>
<xsl:template match="desc_content/*">
<string>{.}</string>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/pNmC4HJ/1
I have the following XML: <a>Text with <b>stuff</b> here</a>
With my code:
<xsl:template match="*[local-name() = 'a'][namespace-uri()=namespace-uri(.)]">
<xsl:value-of select="normalize-space(text()) "/><xsl:text> </xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*[local-name() = 'b'][namespace-uri()=namespace-uri(.)]">
<xsl:value-of select="normalize-space(text())"/><xsl:text> </xsl:text>
<xsl:apply-templates/>
</xsl:template>
I only get the result:
Text with stuff
What I want is:
Text with stuff here.
So how do I handle the remaining text after the <b/> element?
Why is this so complicated? If this is really your XML input:
<a>Text with <b>stuff</b> here</a>
then the following stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/">
<xsl:value-of select="a" />
</xsl:template>
</xsl:stylesheet>
will return the requested* result:
Text with stuff here
--
(*) except for the period at the end, which is not present in the input.
So this is what my XML looks like. It's pretty darn simple, just want to lay out a bunch of links to other XML files with different names/titles for each link of course:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="index.xsl"?>
<playToc>
<play>a_and_c.xml</play>
<play>all_well.xml</play>
<play>as_you.xml</play>
<play>com_err.xml</play>
<play>coriolan.xml</play>
<play>cymbelin.xml</play>
<name>Title 1</name>
<name>Title 2</name>
<name>Title 3</name>
<name>Title 4</name>
<name>Title 5</name>
<name>Title 6</name>
</playToc>
Pretty simple, right? And so here is my XSL:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="playToc">
<html>
<body style="text-align:center;">
<xsl:apply-templates select="play"></xsl:apply-templates>
</body>
</html>
</xsl:template>
<xsl:template match="play">
<xsl:variable name="URL">
<xsl:value-of select="."/>
</xsl:variable>
<xsl:variable name="TITLE">
<xsl:value-of select="../name"/>
</xsl:variable>
<xsl:value-of select="$TITLE"/>
<br />
</xsl:template>
</xsl:stylesheet>
And this is the output:
Title 1
Title 1
Title 1
Title 1
Title 1
Title 1
When I want this to be the output, of course:
Title 1
Title 2
Title 3
Title 4
Title 5
Title 6
Any help would be so great! Thanks a bunch!
Well the XML input is poorly structured but you can work around this by doing
<xsl:template match="play">
<xsl:variable name="pos" select="position()"/>
<a href="{.}">
<xsl:value-of select="../name[position() = $pos]"/>
</a>
<br/>
</xsl:template>
Make sure you keep the <xsl:apply-templates select="play"/> in the other template, otherwise the approach with position() won't work.
<xsl:variable name="TITLE">
<xsl:value-of select="../name"/>
</xsl:variable>
Your problem is here.
The string value of ../name is the string value of the first name child of the parent of the initial context (current) node.
What you actually want is to get the value of the name child that has the same position as the position of the current (play) node.
This complete and short 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="play">
<xsl:variable name="vPos" select="position()"/>
<a href="{.}">
<xsl:value-of select="../name[$vPos]"/>
</a><br />
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the provided source XML document:
<playToc>
<play>a_and_c.xml</play>
<play>all_well.xml</play>
<play>as_you.xml</play>
<play>com_err.xml</play>
<play>coriolan.xml</play>
<play>cymbelin.xml</play>
<name>Title 1</name>
<name>Title 2</name>
<name>Title 3</name>
<name>Title 4</name>
<name>Title 5</name>
<name>Title 6</name>
</playToc>
produces the wanted, correct result:
Title 1
<br/>
Title 2
<br/>
Title 3
<br/>
Title 4
<br/>
Title 5
<br/>
Title 6
<br/>
Do note:
There is no need to use xsl:apply-templates at all.
There is just one template.