pattern not matching though declared - xslt-2.0

I've the below XML.
<root>
<para>
<label>5.</label> In essence, the Court in <star.page>19</star.page>
</para>
<para>
<label><star.page>21</star.page> 13.</label> Frankly, I cannot see how
one can escape
</para>
</root>
and using the below XSLT.
<xsl:template match="para">
<xsl:apply-templates select="./node()[1][self::star.page]|./label/node()[1][self::star.page]" mode="first"/>
</xsl:template>
<xsl:template match="star.page" mode="first">
<xsl:if test="preceding::star.page">
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="."/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
<a name="{concat('pg_',.)}"/>
</xsl:if>
</xsl:template>
here when i try to run this code, the first para star.page is getting caught, but the second star.page, i.e. <para><label><star.page>21</star.page> 13.</label>... is not getting caught. please let me know where am i going wrong. here i'm taking [1], since i want to catch the first occurance.
Thanks

I just tried your code on xmlplayground, both the star.page elements reach the template but the if clause is preventing the first from reaching the output.

Related

Conditional Formatting XSL

I'm trying to make <sup> elements superscripted when I encounter them. I'm iterating over a large file which I can include if required, basically <xml><article><body><p><em></em><sup></sup></p></body></article></xml>
I'm receiving:
Error reported by XML parser: The element type "fo:inline" must be terminated by
the matching end-tag "</fo:inline>"
when trying to use the below to raise the superscripts:
<xsl:for-each select="*">
<fo:block>
<xsl:if test="name() = 'sup'">
<fo:inline vertical-align='super' baseline-shift='4pt'>
</xsl:if>
<xsl:apply-templates select="." mode="xhtml"/>
<xsl:if test="name() = 'sup'">
</fo:inline>
</xsl:if>
</fo:block>
</xsl:for-each>
How can I correct this so the vertical-align='super' is only for sup elements; and is there a better approach to this? I plan to do the same for ems later.
My code which I use currently but puts everything out as plain text is:
<xsl:for-each select="*">
<fo:block><xsl:apply-templates select="." mode="xhtml"/></fo:block>
</xsl:for-each>
If you want to transform <sup></sup> to <fo:inline vertical-align='super' baseline-shift='4pt'></fo:inline> then the usual way with XSLT is to set up a template
<xsl:template match="sup">
<fo:inline vertical-align='super' baseline-shift='4pt'>
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
I am not sure whether you want to do that in general or for a particular mode (in that case add mode="mode-name" on the xsl:template and mode="#current" on the xsl:apply-templates).

Return a value type of element from a function in XSLT

This is my XML
<report>
<format-inputs>
<narrative-entity-ids>
<entity id="28495795" type-cdf-meaning="DIAGNOSES"/>
<entity id="28495741" type-cdf-meaning="DIAGNOSES"/>
<entity id="28495471" type-cdf-meaning="DIAGNOSES"/>
</narrative-entity-ids>
</format-inputs>
</report>
I am creating a function in commonFunction.xslt
<xsl:function name="cdocfx:createEntityIdList" >
<xsl:param name="formatInputsNodes"/>
<xsl:if test="fn:exists(n:report/n:format-inputs)"
<xsl:variable name="entityIdList" as="element()*">
<xsl:for-each select="$formatInputsNodes/n:narrative-entity-ids/n:entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
</xsl:variable>
</xsl:if>
<xsl:copy-of select="$entityIdList"/>
</xsl:function>
I am calling this function in other xslt file where commonFunction.xslt was included
<xsl:variable name="entityIdList" select="cdocfx:createEntityIdList(n:report/n:format-inputs)"/>
</xsl:variable>
My question is variable entityIdList should be value type of element but it is having the document-node type how can i achieve this ??
Please provide minimal but complete samples of XML input, XSLT you have, output you want and the one you get together with any exact error messages you have encountered.
I am currently not sure I understand what you want to achieve, if you construct a variable of type element()* you seem to want to construct a sequence of element nodes. Any xsl:value-of however will only output the string values of the selected items in a text node so it is not clear why you first construct elements if you only want to output string values. If you construct nodes and want to output them use xsl:copy-of or xsl:sequence, not xsl:value-of.
To show two examples of writing a function that returns a sequence of elements (i.e. whose result is of type element()*) I have set up https://xsltfiddle.liberty-development.net/3NzcBtE which has two functions
<xsl:function name="mf:ex1">
<xsl:param name="input" as="element()*"/>
<xsl:for-each select="$input">
<item>{ #id }</item>
</xsl:for-each>
</xsl:function>
<xsl:function name="mf:ex2">
<xsl:param name="input" as="element()*"/>
<xsl:variable name="elements" as="element()*">
<xsl:for-each select="$input">
<item>{ #id }</item>
</xsl:for-each>
</xsl:variable>
<xsl:sequence select="$elements"/>
</xsl:function>
the first simply directly constructs some result elements in the function body, that way the result is a sequence of element nodes. The second function uses your approach of constructing a sequence of elements nodes in a variable, the proper way to return that variable value then from the function is to use xsl:sequence.
It is not clear at which position of the posted code you think are dealing with a document-node() node.
Note also that
<xsl:choose>
<xsl:when test="fn:exists($formatInputsNodes/n:narrative-entity-ids)">
<xsl:for-each select="$formatInputsNodes/n:narrative-entity-ids/n:entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
</xsl:when>
</xsl:choose>
can be simplified to
<xsl:for-each select="$formatInputsNodes/n:narrative-entity-ids/n:entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
As you have now presented an XML input that is at least well-formed and some XSLT snippets (that are unfortunately not well-formed and seem to use a namespace although the XML input shown doesn't use one) here is an attempt to morph that into a working sample
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:cdocfx="http://example.com/cdox-functions"
exclude-result-prefixes="#all"
version="3.0">
<xsl:function name="cdocfx:createEntityIdList" >
<xsl:param name="formatInputsNodes"/>
<xsl:variable name="entityIdList" as="element()*">
<xsl:for-each select="$formatInputsNodes/narrative-entity-ids/entity">
<Item><xsl:value-of select="#id"/></Item>
</xsl:for-each>
</xsl:variable>
<xsl:copy-of select="$entityIdList"/>
</xsl:function>
<xsl:variable name="entityIdList" select="cdocfx:createEntityIdList(report/format-inputs)"/>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="$entityIdList instance of element()*, $entityIdList" separator=", "/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/pPqsHTW/1
Output there for the check $entityIdList instance of element()* is true so I am not sure why you say you have a document node.

how to match the case sensitive characters in elements

I want to match the two or more case-sensitive characters in element of familName in author element. If found the ERROR message should be shown otherwise the familyName content will shown. The below code is not viewed in browser. Please check. I have used the xsl version is 2.0.
XML CODE
<author><familyName>CH</familyName> <givenNames>JC</givenNames></author>
XSLT CODE
<xsl:for-each select="author">
<xsl:choose>
<xsl:when test="matches(familyName,'([A-Z]){{1,}}')"><xsl:text>ERROR</xsl:text></xsl:when>
<xsl:otherwise>
<xsl:value-of select="familyName"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
Its working for me. You can write a specific template for showing error:
<xsl:template match="familyName[matches(.,'^[A-Z][A-Z]+')]">
<xsl:text>ERROR</xsl:text>
</xsl:template>

Simple XSLT Transformation When XML elements have text nodes and child nodes

I am new to xslt and am trying to transform the following xml:
<li>Hi there <person>David</person> , how is it going? </li>
I would like to transform this to another xml to something like:
<response>Hi there PERSON_NAME , how is it going? </response>
What I have so far is this:
<xsl:template match="li">
<response><xsl:value-of select="text()"/>
<xsl:choose>
<xsl:when test="person">
<xsl:text> PERSON_NAME </xsl:text>
</xsl:when>
</xsl:choose>
</response>
</xsl:template>
This is the output I get:
<response>Hi there , how is it going? PERSON_NAME</response>
Not exactly what I wanted. I am new to xslt and read a book. I did not find any example where there was a situation where an xml element had a child node in between its text value. Not sure if xslt can handle this or I am missing something fundamental. Any help would be greatly appreciated. I am using xslt 2.0
You can simply define two template to handle your condition.
<xsl:template match="li">
<response>
<xsl:apply-templates/>
</response>
</xsl:template>
<xsl:template match="person">
<xsl:text>PERSON_NAME</xsl:text>
</xsl:template>

Eliminate line-breaks with XSLT 2.0 analyze-string

I use the XSLT 2.0 element analyze-string in a stylesheet that transforms XML to HTML; specifically, I use it to convert string encoding for subscripts in chemical formulae to HTML subscripts. Therefore, the result is a string, to go in a p or td element, with embedded mark-up.
The transformation is supposed to produce output like H2O but in fact inserts a line-break in the HTML:
H
<sub>2</sub>O
and this break is (correctly) interpreted by the browser as a space:
H
2O
which is ugly.
Is there a way to remove the line-break? I've tried putting the whole analyze-string element on one line and that doesn't work.
The input would be something like
<OrdinaryStructralFormula>H$_2$O</OrdinaryStructuralFormula>
for a simple case and
<OrdinaryStructralFormula>C$_2$OH$_5$$^-</OrdinaryStructuralFormula>
for a more-complicated one. Note that the subscript pattern can match multiple times in the general case and can be either in the middle or at the end of the string. The pattern also has to match and eliminate any notation for charge: the $^- bit at the end of the second example.
The XSLT processor is Saxon 9.4 and the XSLT template follows.
<xsl:template name="formula">
<xsl:param name="formula"/>
<xsl:if test="$formula">
<xsl:variable name="f" select="translate($formula, '$', '')"/>
<xsl:analyze-string select="$f" regex="(_)(\d+)|(\^)\d*\+|(\^)\d*\-">
<xsl:matching-substring>
<xsl:if test="regex-group(1)='_'">
<sub><xsl:value-of select="regex-group(2)"/></sub>
</xsl:if>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:if>
</xsl:template>
I cannot reproduce the reported result.
This transformation (which is what you should have given us, but you only provided a template):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:call-template name="formula">
<xsl:with-param name="formula" select="/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="formula">
<xsl:param name="formula"/>
<xsl:if test="$formula">
<xsl:variable name="f" select="translate($formula, '$', '')"/>
<xsl:analyze-string select="$f" regex="(_)(\d+)|(\^)\d*\+|(\^)\d*\-">
<xsl:matching-substring>
<xsl:if test="regex-group(1)='_'">
<sub><xsl:value-of select="regex-group(2)"/></sub>
</xsl:if>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document with Saxon 9.1.05:
<formula>H$_2$O</formula>
produces the wanted, correct result:
H<sub>2</sub>O
When the same transformation is applied on the second XML document:
<OrdinaryStructuralFormula>C$_2$OH$_5$$^-</OrdinaryStructuralFormula>
Again the wanted correct result is produced:
C<sub>2</sub>OH<sub>5</sub>
Do note: I ran the same transformations with two other XSLT 2.0 processors: XQSharp (XMLPrime) and AltovaXML (XML-SPY) and got exactly the same, correct results.

Resources