Get text in String using XSLT - xslt

Get text in String using XSLT
Input :
<chapter href="Sapmle_text" format="ditamap"
otherprops="navlabel(Reading) navnum(41)" class="- map/topicref bookmap/chapter ">
Output should be:
41
Tried Xpath:
//xref[parent::p/following-sibling::fig]/ancestor::chapter/substring-after(#outputclass,'navnum(')
But my tried code is not working properly. How can I get 41 from chapter element?
I am using XSLT 2.0

Try:
chapter/substring-before(substring-after(#otherprops, 'navnum('), ')')

You are using DITA and trying to get generalized attribute value from chapter/#otherprops. So it will be worth to develop general function that gets generalized attribute value from specified attribute using XSLT 3.0. (You can use XSLT 3.0 in DITA-OT 3.x. without no problem.)
[Input File]
<?xml version="1.0" encoding="UTF-8"?>
<bookmap>
<booktitle>
<mainbooktitle>Test</mainbooktitle>
</booktitle>
<chapter href="Sapmle_text" format="ditamap"
otherprops="navlabel(Reading) navnum(41)" class="- map/topicref bookmap/chapter "/>
</bookmap>
[Stylesheet 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"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:ahf="http://www.antennahouse.com/names/XSLT/Functions/Document"
exclude-result-prefixes="xs math ahf"
version="3.0">
<xsl:template match="/">
<xsl:variable name="navnumVal" as="xs:string?" select="ahf:getGeneralizedAttVal(/descendant::chapter[1]/#otherprops,'navnum')"/>
<xsl:message select="'navnum=' || $navnumVal"/>
</xsl:template>
<!--
function: Get generalized form attribute value
param: $prmAtt: #audience, #platform, #product, or #otherprops
$prmGeneralizedAttName: Generalized attribute name
return: xs:string?: Generalized attribute value
note: See
http://docs.oasis-open.org/dita/dita/v1.3/errata02/os/complete/part3-all-inclusive/archSpec/base/generalization-attributes.html#attributegeneralize
-->
<xsl:function name="ahf:getGeneralizedAttVal" as="xs:string?">
<xsl:param name="prmAtt" as="attribute()"/>
<xsl:param name="prmGeneralizedAttName" as="xs:string"/>
<xsl:variable name="attVal" as="xs:string" select="$prmAtt => string() => normalize-space()"/>
<xsl:variable name="regx" as="xs:string" select="$prmGeneralizedAttName || '\((.+)\)'"/>
<xsl:analyze-string select="$attVal" regex="{$regx}">
<xsl:matching-substring>
<xsl:sequence select="regex-group(1)"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:function>
</xsl:stylesheet>
[Output result]
navnum=41

Related

How to Derive/Calculate Upper/Lower Corner in GML Envelope with XSL from PosList XML

Seeking advice on how to calculate the lowerCorner and upperCorner in GML Envelope given an array (coordinates) in the XML. Note I have reduced the list of coordinates significantly to keep short.
Aware I need to iterate the <coordinates> element but unsure most efficient way to calculate the lowerCorner and upperCorner pairs to eventually map into the GML Envelope.
XML Sample
<?xml version="1.0" encoding="UTF-8"?>
<Extract>
<n1:XMLExtract xmlns:n1="urn:com:aaa">
<regionId>4671</regionId>
<coordinates>151.344553 -33.4123250000193, 151.346606 -33.4126370000193, 151.347188 -33.4127280000193, 151.347707 -33.4127990000193, 151.347858 -33.4121160000193, 151.34931 -33.4123270000192, 151.349253 -33.4125910000192, 151.349693 -33.4126610000193, 151.34963 -33.4129810000192, 151.351338 -33.4132280000193, 151.351393 -33.4129550000193, 151.352038 -33.4130480000192, 151.352169 -33.4128100000193, 151.352355 -33.4128370000193, 151.35249 -33.4128910000193, 151.352585 -33.4129170000193, 151.352913 -33.4130080000193, 151.35294 -33.4131310000193, 151.355307 -33.4134860000192, 151.355315 -33.4134470000193, 151.355764 -33.4135020000193, 151.355757 -33.4135590000193, 151.356196 -33.4136240000192, 151.356229 -33.4134890000192, 151.356342 -33.4136260000193, 151.358407 -33.4139280000192, 151.358335 -33.4142510000192, 151.358465 -33.4143660000193, 151.359572 -33.4145260000194, 151.359936 -33.4144860000193, 151.360146 -33.4146080000193, 151.360627 -33.4146790000192, 151.360619 -33.4146980000193, 151.362603 -33.4149980000193, 151.362996 -33.4150940000193, 151.363655 -33.4158080000193, 151.364236 -33.4161380000194, 151.365691 -33.4163460000193, 151.366212 -33.4164920000193, 151.367333 -33.4170870000193, 151.368456 -33.4180250000193, 151.368481 -33.4180200000193, 151.368888 -33.4183130000193, 151.371305 -33.4187840000193, 151.373106 -33.4187890000193, 151.374004 -33.4189970000194, 151.374994 -33.4194460000193, 151.376513 -33.4199650000193, 151.378063 -33.4197680000193, 151.379519 -33.4185780000193, 151.383555 -33.4161210000193, 151.393929 -33.4059400000192, 151.396063 -33.4062720000193, 151.396727 -33.4051740000192, 151.39785 -33.4032380000193, 151.397122 -33.4027200000192, 151.396761 -33.4022700000193, 151.396541 -33.4008350000192, 151.397496 -33.3995910000192, 151.397788 -33.3990280000193, 151.397788 -33.3990100000192, 151.397773 -33.3990000000192, </coordinates>
<interactionId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="1" />
<interactionTypeId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="1" />
<refNumber/>
<incidentNumber/>
<payloadId>20002065</payloadId>
<filename/>
<url/>
</n1:XMLExtract>
</Extract>
Desired output as follows:
<gml:boundedBy>
<gml:Envelope srsDimension="2" srsName="EPSG:4283">
<gml:lowerCorner>-30.511985 151.63592</gml:lowerCorner>
<gml:upperCorner>-30.49207 151.669169</gml:upperCorner>
</gml:Envelope>
</gml:boundedBy>
Assuming you want to find the coordinates of the rectangle bounding your polygon, and assuming your processor supports the EXSLT extension functions str:split(), math:min() and math:max() (IOW, you are using either libxslt or Xalan), you could do something like:
XSLT 1.0 + EXSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:n1="urn:com:aaa"
xmlns:exsl="http://exslt.org/common"
xmlns:math="http://exslt.org/math"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="exsl math str"
exclude-result-prefixes="n1">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Extract">
<xsl:variable name="vertices-rtl">
<xsl:for-each select="str:split(n1:XMLExtract/coordinates, ', ')">
<v x="{substring-before(., ' ')}" y="{substring-after(., ' ')}"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="vertices" select="exsl:node-set($vertices-rtl)/v" />
<output>
<lowerLeft>
<xsl:value-of select="math:min($vertices/#x)" />
<xsl:text> </xsl:text>
<xsl:value-of select="math:min($vertices/#y)" />
</lowerLeft>
<upperRight>
<xsl:value-of select="math:max($vertices/#x)" />
<xsl:text> </xsl:text>
<xsl:value-of select="math:max($vertices/#y)" />
</upperRight>
</output>
</xsl:template>
</xsl:stylesheet>
Applied to your example input, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<lowerLeft>151.344553 -33.4199650000193</lowerLeft>
<upperRight>151.39785 -33.3990000000192</upperRight>
</output>

How to use variable as XPath in XSLT test

I have a function that accepts a parameter $path. It should contain an XPath expression, and my goal is to test whether or not the node at the end of the expression is valid.
However, when I try to do
<xsl:function name="testPath">
<xsl:param name="path">
<xsl:if test="$path">
it tests $path as a string, and not as an XPath expression (meaning that it returns true if $path is not empty). If I hardcode a XPath expression in, then it does the check properly.
I am using XPath 2.0
How do I use a variable as an XPath expression?
Dynamic XPath evaluation is an optional feature supported in XSLT 3.0. Here is an example using Saxon 9.5 PE:
<xsl:stylesheet
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.org/mf"
exclude-result-prefixes="xs mf">
<xsl:output method="text"/>
<xsl:function name="mf:testPath" as="xs:boolean">
<xsl:param name="context-node" as="node()"/>
<xsl:param name="path" as="xs:string"/>
<xsl:variable name="seq" as="item()*">
<xsl:evaluate xpath="$path" context-item="$context-node"/>
</xsl:variable>
<xsl:sequence select="exists($seq)"/>
</xsl:function>
<xsl:template match="/">
<xsl:value-of select="('a/b[#id = "b2"]', 'a/c') ! mf:testPath(current(), .)" separator="
"/>
</xsl:template>
</xsl:stylesheet>
Evaluated against the input sample
<a>
<b id="b1">foo</b>
<b id="b2">bar</b>
</a>
it outputs
true
false

Text value of input xml element as final xslt output

I have a scenario where the input(source) xml is having an element which contains a valid well formed xml as string. I am trying to write an xslt that would give me the text value of that desired element which contains the payload xml. In essence, output should only be text of the element that contains it. Here is what I am trying, am I missing something obvious here. I am using xslt 1.0
Thanks.
Input xml:
<BatchOrders xmlns="http://Microsoft.ABCD.OracleDB/STMT">
<BatchOrdersRECORD>
<BatchOrdersRECORD>
<ActualPayload>
<PersonName>
<PersonGivenName>CaptainJack</PersonGivenName>
<PersonMiddleName>Walter</PersonMiddleName>
<PersonSurName>Sparrow</PersonSurName>
<PersonNameSuffixText>Sr.</PersonNameSuffixText>
</PersonName>
</ActualPayload>
</BatchOrdersRECORD>
</BatchOrdersRECORD>
</BatchOrders>
Xslt:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="text()|#*" name="sourcecopy" mode="xml-to-string">
<xsl:value-of select="*"/>
</xsl:template>
<xsl:template name="xml-to-string-called-template">
<xsl:param name ="param1">
<xsl:element name ="DestPayload">
<xsl:text disable-output-escaping ="yes"><![CDATA[</xsl:text>
<xsl:call-template name ="sourcecopy"/>
<xsl:text disable-output-escaping ="yes">]]></xsl:text>
</xsl:element>
</xsl:param>
</xsl:template>
</xsl:stylesheet>
Desired Output:
<PersonName>
<PersonGivenName>CaptainJack</PersonGivenName>
<PersonMiddleName>Walter</PersonMiddleName>
<PersonSurName>Sparrow</PersonSurName>
<PersonNameSuffixText>Sr.</PersonNameSuffixText>
</PersonName>
Do you really need the mode="xml-to-string"?
Change
<xsl:template match="text()|#*" name="sourcecopy" mode="xml-to-string">
<xsl:value-of select="*"/>
</xsl:template>
to
<xsl:template match="text()|#*" name="sourcecopy">
<xsl:value-of select="." disable-output-escaping ="yes"/>
</xsl:template>
Would this template suffice?

XSLT issue with reading in non-xml data

I am newbie to XSLT.
I have a requirement to read a URL and convert some of its values into XML.
I wrote a XSLT that has to take URL as the input value and create a XML file from some of the content of the URL value.
When I debugged the XSLT in XMLSPY, I noticed that the URL value is not being picked up by inputValue variable in the below code. I am not sure if my approach to input the URL and the template match are wrong.
Any help is appreciated.
Thanks in advance.
Input to XSLT:
http://host:port/abc/xyz1/6xyz6?qq=123&pp=3
Here the XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:nnc="Nnc" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:param name="inVal" select="xs:string(http://host:port/abc/xyz1/6xyz6?qq=123&pp=3)"/>
<xsl:template match="/">
<xsl:variable name="inputValue" select="$inVal"/>
<xsl:if test="string-length($inputValue)=0">
<xsl:message terminate="yes">
inputValue is blank
</xsl:message>
</xsl:if>
<xsl:variable name="value" as="xs:string" select="substring-after($inputValue, 'abc/' )"/>
<xsl:variable name="tokenizedValues" select="tokenize($value,'/')"/>
<xsl:for-each select="$tokenizedValues">
<xsl:if test="position() = 1">
<id>
<xsl:value-of select="."/>
</id>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The desired XML output:
<?xml version="1.0" encoding="UTF-8"?>
<id>6xyz6</id>
<qq>123</qq>
<pp>123</pp>
Well if you want to pull in a text file then with XSLT 2.0 and later you can do that but not by simply using a URL, you need to call the unparsed-text function e.g.
<xsl:variable name="inputData" as="xs:string" select="unparsed-text('http://example.com/foo')"/>
See http://www.w3.org/TR/xslt20/#unparsed-text, depending on the encoding of your text document you need to add a second parameter when calling the function.

XSLT key() lookup

I'm trying out a sample of look-up tables in XSLT and am not able to get it to work
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" />
<xsl:key name="classification-lookup" match="classification" use="id" />
<xsl:variable name="classification-top" select="document('')/*/classifications" />
<xsl:template match="BusinessListing">
<listing>
<id>
<xsl:value-of select="id" />
</id>
<xsl:apply-templates select="$classification-top">
<xsl:with-param name="curr-label" select="." />
</xsl:apply-templates>
</listing>
</xsl:template>
<xsl:template match="classifications">
<xsl:param name="curr-label" />
<category>
<xsl:value-of select="key('classification-lookup', $curr-label/listingData/classifications/classificationId)/description" />
</category>
</xsl:template>
<classifications>
<classification>
<id>7981</id>
<description>Category1</description>
</classification>
<classification>
<id>7982</id>
<description>Category2</description>
</classification>
<classification>
<id>7983</id>
<description>Category3</description>
</classification>
<classification>
<id>7984</id>
<description>Category4</description>
</classification>
</classifications>
</xsl:stylesheet>
and the source is as below .
<?xml version="1.0"?>
<BusinessListings>
<BusinessListing>
<id>1593469</id>
<listingData>
<classifications>
<classificationId>7982</classificationId>
<classificationId>7983</classificationId>
</classifications>
</listingData>
</BusinessListing>
</BusinessListings>
In the result below , The category is empty but I need the Classification Id from the source to be matched with the id in the classification tag and the category generated .
<?xml version="1.0" encoding="UTF-8"?>
<listing>
<id>1593469</id> -- Empty I need the Category2 and Category3 here
<category/>
</listing>
I know that I may be wide off mark but I've just started off with XSLT and referred the sample here http://www.ibm.com/developerworks/xml/library/x-xsltip.html . Thanks for the help .
Your XSLT stylesheet contains an error -- according to spec, any child-element of xsl:stylesheet (aka top-level element) must be in a non-null namespace:
"*In addition, the xsl:stylesheet
element may contain any element not
from the XSLT namespace, provided that
the expanded-name of the element has a
non-null namespace URI. "
If the XSLT processor you are using doesn't raise an error, then it is non-compliant and buggy and shouldn't be used. Find and use a compliant XSLT processor (I am using .NET XslCompiledTransform, Saxon 6.5.5, ..., etc).
There are other errors, too.
Solution:
Define a new namespace with prefix (say) "x:":
Change the embedded <classifications> to <x:classifications> -- now this conforms to the Spec.
Perform more changes to the code until you get this transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="my:x" exclude-result-prefixes="x">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="classification-lookup" match="classification"
use="id" />
<xsl:template match="BusinessListing">
<listing>
<id>
<xsl:value-of select="id" />
</id>
<xsl:apply-templates/>
</listing>
</xsl:template>
<xsl:template match="classificationId">
<xsl:variable name="vCur" select="."/>
<xsl:for-each select="document('')">
<category>
<xsl:value-of select=
"key('classification-lookup',$vCur)/description" />
</category>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()"/>
<x:classifications>
<classification>
<id>7981</id>
<description>Category1</description>
</classification>
<classification>
<id>7982</id>
<description>Category2</description>
</classification>
<classification>
<id>7983</id>
<description>Category3</description>
</classification>
<classification>
<id>7984</id>
<description>Category4</description>
</classification>
</x:classifications>
</xsl:stylesheet>
.4. In the above code notice the line: <xsl:for-each select="document('')"> .
The purpose of this is to make the stylesheet the current document. The key() function operates only on the current document and if you want the embedded classification elements to be indexed and used, you must change the current document (usually in this way). In XSLT 2.0 the key() function allows a 3rd argument which is a node from the document whose index should be used.
When this transformation is applied to the provided XML document:
<BusinessListings>
<BusinessListing>
<id>1593469</id>
<listingData>
<classifications>
<classificationId>7982</classificationId>
<classificationId>7983</classificationId>
</classifications>
</listingData>
</BusinessListing>
</BusinessListings>
the wanted, correct result is produced:
<listing>
<id>1593469</id>
<category>Category2</category>
<category>Category3</category>
</listing>