I'd like to use itemized list instead of hard breaks, see the example:
<para>line1<?linebreak?>
line2<?linebreak?>
line3</para>
However, I am experiencing weird behavior in my recursive template which prevents processing the second line correctly. I've created simplified test case - not recursive any more. If count(preceding::processing-instruction('linebreak')) = 0 expression is used this way, nothing is returned, but I would expect the second line.
<line>line1</line><node>
line2<?linebreak?>
line3</node>
line2
That <node> element is for debugging purposes here. It confirms I process expected data.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="para[processing-instruction('linebreak')]">
<xsl:call-template name="getLine">
<xsl:with-param name="node" select="./node()"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="getLine">
<xsl:param name="node"/>
<line>
<xsl:copy-of
select="$node/self::processing-instruction('linebreak')[not(preceding::processing-instruction('linebreak'))]/preceding::node()"
/>
</line>
<xsl:call-template name="getSecondLine">
<xsl:with-param name="node"
select="$node/self::processing-instruction('linebreak')[not(preceding::processing-instruction('linebreak'))]/following::node()"
/>
</xsl:call-template>
</xsl:template>
<xsl:template name="getSecondLine">
<xsl:param name="node"/>
<node>
<xsl:copy-of select="$node"/>
</node>
<xsl:copy-of
select="$node/self::processing-instruction('linebreak')[count(preceding::processing-instruction('linebreak')) = 0]/preceding::node()"
/>
</xsl:template>
</xsl:stylesheet>
Tested in Saxon HE/EE 9.6.0.7 (in Oxygen XML Editor 18).
The processing of the first linebreak works correctly:
<line>
<xsl:copy-of
select="$node/self::processing-instruction('linebreak')
[not(preceding::processing-instruction('linebreak'))]
/preceding::node()"/>
</line>
though only on this sample; on more complex data, you would get the wrong results because you should be using the preceding-sibling axis rather than the preceding axis.
But the code could be greatly simplified, I would write the select expression as:
select="$node[self::processing-instruction('linebreak')][1]
/preceding-sibling::node()"
The processing of the second linebreak seems very confused. You are passing the parameter
$node/self::processing-instruction('linebreak')
[not(preceding::processing-instruction('linebreak'))]
/following::node()"
which is effectively
select="$node[self::processing-instruction('linebreak')][1]
/following-sibling::node()"
which selects the three nodes
line2<?linebreak?>line3
(plus whitespace) which you are outputting within a <node> element, producing
<node>line2<?linebreak?>line3</node>
(again ignoring whitespace)
and then you do
select="$node/self::processing-instruction('linebreak')
[count(preceding::processing-instruction('linebreak'))=0]
/preceding::node()"
Here $node/self::processing-instruction('linebreak') selects the second of these three nodes, which is the second linebreak processing instruction. The count of preceding (or preceding-sibling) processing instructions is 1, because the one you are dealing with is the second.
I'm not quite sure what you were thinking of, but I suspect your mistake is to think of "preceding" and "following" as selecting relative to the position of the node within the $node sequence, rather than relative to other nodes within the original source tree. I would recommend reading the section of an XPath reference book that describes the various axes.
Related
I like to extract chapter numbers, their title and their description from an XML file to an XML element/attribute hierarchy. They are distributed in continuous text in different elements. The XML looks like this:
<?xml version="1.0" encoding="utf-8"?>
<root>
<cell>3.1.1.17 First Section The “First appropriate” section lists things that can occur when an event happens. All of these event conditions result in an error.
</cell>
<cell>3.1.1.18 Second Section This section lists things that occur under certain conditions. 3.1.1.19 Third Section This section lists events that occur within a specific space. 3.2 SPACE chapter provides descriptions other stuff. See also: Chapter 4, “Other Stuff Reference” in the Manual.
</cell>
</root>
The desired output should look like this:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Desc chapter="3.1.1.17" title="First Section">The “First appropriate” section lists things that can occur when an event happens. All of these event conditions result in an error.</Desc>
<Desc chapter="3.1.1.18" title="Second Section">This section lists things that occur under certain conditions.</Desc>
<Desc chapter="3.1.1.19" title="Third Section">This section lists events that occur within a specific space. 3.2 SPACE chapter provides descriptions other stuff. See also: Chapter 4, “Other Stuff Reference” in the Manual.</Desc>
</Root>
My XSLT so far is:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" encoding="utf-8" />
<xsl:template match="text()" />
<xsl:template match="/root">
<Root>
<xsl:apply-templates select="cell" />
</Root>
</xsl:template>
<xsl:template match="cell">
<xsl:variable name="sections" as="element(Desc)*">
<xsl:analyze-string regex="(\d+\.\d+\.\d+\.\d+)\s(.*?Section)(.*?)" select="text()">
<xsl:matching-substring>
<Desc chapter="{regex-group(1)}" title="{regex-group(2)}">
<xsl:value-of select="regex-group(3)" />
</Desc>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:for-each select="$sections">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The problem is situated in the last part of the RegEx: (.*?) - a non-greedy consuming expression. Unfortunately I can't make it stop at the right position. I tried to use ?: and (?=...) to make it stop non-consuming before the next \d+\.\d+\.\d+\.\d+\., but it seems the RegEx syntax of XSLT-2.0 is somewhat different from other dialects.
How would I extract the relevant parts to conveniently process them in the for-each as regex-group(1..3)?
And, additionally, I am interested in a pretty complete XSLT-2.0 reference of all RegEx-tokens.
It seems
<xsl:template match="cell">
<xsl:variable name="sections">
<xsl:analyze-string regex="(\d+\.\d+\.\d+\.\d+)\s(.*?Section)" select=".">
<xsl:matching-substring>
<xsl:message select="concat('|', regex-group(3), '|')"/>
<Desc chapter="{regex-group(1)}" title="{regex-group(2)}">
<xsl:value-of select="regex-group(3)" />
</Desc>
</xsl:matching-substring>
<xsl:non-matching-substring>
<Value>
<xsl:value-of select="."/>
</Value>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:for-each select="$sections/Desc">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:value-of select="following-sibling::Value[1]"/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
captures both the data you want to select and the trailing text.
Sorry that i have to reply in JS but i trust you can simply figure out what's going on. Your regex and replace solution should be like this;
var xmlData = '<?xml version="1.0" encoding="utf-8"?>\n<root>\n <cell>3.1.1.17 First Section The “First appropriate” section lists things that can occur when an event happens. All of these event conditions result in an error.\n </cell>\n <cell>3.1.1.18 Second Section This section lists things that occur under certain conditions. 3.1.1.19 Third Section This section lists events that occur within a specific space. 3.2 SPACE chapter provides descriptions other stuff. See also: Chapter 4, “Other Stuff Reference” in the Manual.\n </cell>\n</root>',
rex = /<cell>(?:\s*(\d+.\d+.\d+.\d+)\s+(\w+)\s+Section)(.+)\n*\s*<\/cell>/gm,
xml = xmlData.replace(rex,'<Desc chapter="$1" title="$2 Section">$3</desc>');
console.log(xmlData);
<?xml version="1.0" encoding="utf-8"?>
<root>
<Desc chapter="3.1.1.17" title="First Section"> The “First appropriate” section lists things that can occur when an event happens. All of these event conditions result in an error.</desc>
<Desc chapter="3.1.1.18" title="Second Section"> This section lists things that occur under certain conditions. 3.1.1.19 Third Section This section lists events that occur within a specific space. 3.2 SPACE chapter provides descriptions other stuff. See also: Chapter 4, “Other Stuff Reference” in the Manual.</desc>
</root>
I'm trying to split a tab separated value string using substring-before I'm having a hard time because the XSLT mediator does not work as it should.
The xslt stylesheet:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:p576="http://p576.test.ws" version="1.0">
<xsl:output encoding="UTF-8" method="xml" indent="yes"></xsl:output>
<xsl:param name="NAMESPACE"></xsl:param>
<xsl:param name="LOG_ID"></xsl:param>
<xsl:template match="/">
<xsl:element name="Response" namespace="{$NAMESPACE}">
<xsl:if test="$LOG_ID">
<xsl:element name="LogId" namespace="{$NAMESPACE}">
<xsl:value-of select="$LOG_ID"></xsl:value-of>
</xsl:element>
</xsl:if>
<xsl:element name="Text" namespace="{$NAMESPACE}">
<xsl:value-of select="substring-before(//p576:execMRPCResponse/p576:execMRPCReturn, ' ')"></xsl:value-of>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The string:
999900418559 59 4730 Payment Created & Posted
Instead of cutting the string "999900418559" it cuts it here: "999900418559 59 4730 Payment" where a space is. The same result is achieved if you use substring-before '$#32;' meaning tab and space are converted to space when looking into the string.
Is there any way to keep this from happening? or if the problem is with the version of saxon used by wso2, should I update it (I'm using WSO2ESB v4.8.1)?
If tabs in the source document are being converted to spaces, then this is happening before Saxon gets to see the data. It could happen, for example, if the data is put through a schema validator and the type of the relevant element uses a whitespace facet of "collapse".
To prevent it happening, you first need to find out when and where it is happening, and there are no clues to that in your post (at least for someone who has never heard of WSO2ESB).
As a first diagnostic step, try to capture the exact form of the source document supplied as input to the transformation, and check the state of the whitespace it contains.
Having written that, I think there is another possibility, which is that the character reference in your stylesheet is being corrupted by whatever process it is that compiles the stylesheet. You could defend against that by replacing ' ' with codepoints-to-string(9).
What I want to do is given an element as context, I want to determine if it has a child with a given name and determine if that child has a node with a given name so I can do operations with it. It is important that I do this in XPath 1.0 syntax.
The code that I've gotten so far is this.
<xsl:for-each select="child::*">
<xsl:if test="contains(name(), 'description')">
<xsl:for-each select="child::*">
<xsl:if test="contains(name(), 'text')">
<xsl:value-of select="node()"/>
</xsl:if>
</xsl:for-each>
</xsl:if>
</xsl:for-each>
It works, but it's big and ugly and I know that there's a way to condense it. The for-eachs there are unnecessary, since I'm only expecting one child node to be named description, and for it to only have one text node.
I feel like this solution should work
<xsl:for-each select="./description/text">
..
</xsl:for-each>
But it isn't, and I'm not really good enough with XPath Syntax to know why.
The reason I'm asking is because though I've found answers that detect whether a child node has a name, and I've found answers that can get to that child node's context, I haven't found an answer that combines the two, though maybe I just haven't been searching hard enough, in which case I apologize.
Edit: Woops, sorry yeah I forgot to mention that the contains() part of the code was also just a hack because I wasn't sure how to compare their values with equality.
Also as long as the answer is there, <xsl:for-each select="description/text"> does not work either.
A sample of the XML in question is this
<leaf>
<description>
<text> Various Words
</text>
</description>
</leaf>
where the context is the leaf and I am trying to get to the text node.
Edit: The Second Coming:
The problem for me was that my XSLT file was using a default namespace (in my case named a). If I had added that then Borodin's answer would have been correct.
To be specific, this is the code which ended up working for me in the end, in case anyone wants to know.
<xsl:for-each select="a:description/a:text>
<xsl:value-of select="node()"/>
</xsl:for-each>
Thanks Guys ^-^
Do you really want to check whether the element names contain those strings? Or, as your narrative says, do you want elements with that exact name?
To do something like what you have already written, use
<xsl:for-each select="*[contains(name(), 'description')]/*[contains(name(), 'text')]">
<xsl:value-of select="node()"/>
</xsl:for-each>
But if you know the complete names it is a lot neater:
<xsl:for-each select="description/text">
<xsl:value-of select="node()"/>
</xsl:for-each>
If that doesn't work then we need to see more of your source XML and your transform.
Update
If I use this XML
<leaf>
<description>
<text>Various Words</text>
</description>
<description>
<text>More Words</text>
</description>
<description>
<text>Other Words</text>
</description>
</leaf>
and apply this 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"/>
<xsl:template match="/leaf">
<xsl:for-each select="description/text">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
the output is the expected Various WordsMore WordsOther Words. I don't know how to help you unless you describe your situation better, except to say that transforms should be written with another template rather than for-each wherever possible. Like this variation which produces the same output as above.
<?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"/>
<xsl:template match="/leaf">
<xsl:apply-templates select="description/text"/>
</xsl:template>
<xsl:template match="text">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
using pure XSLT 1.0, how can I conditionally assign the node. I am trying something like this but it's not working.
<xsl:variable name="topcall" select="//topcall"/>
<xsl:variable name="focusedcall" select="//focusedcall" />
<xsl:variable name="firstcall" select="$topcall | $focusedcall"/>
For variable firstcall, I am doing the conditional node selection. if there is a topcall then assign it to firstcall, othersie assign firstcall to the focusedcall.
This should work:
<xsl:variable name="firstcall" select="$topcall[$topcall] |
$focusedcall[not($topcall)]" />
In other words, select $topcall if $topcall nodeset is non-empty; $focusedcall if $topcall nodeset is empty.
Re-Update regarding "it can be 5-6 nodes":
Given that there may be 5-6 alternatives, i.e. 3-4 more besides $topcall and $focusedcall...
The easiest solution is to use <xsl:choose>:
<xsl:variable name="firstcall">
<xsl:choose>
<xsl:when test="$topcall"> <xsl:copy-of select="$topcall" /></xsl:when>
<xsl:when test="$focusedcall"><xsl:copy-of select="$focusedcall" /></xsl:when>
<xsl:when test="$thiscall"> <xsl:copy-of select="$thiscall" /></xsl:when>
<xsl:otherwise> <xsl:copy-of select="$thatcall" /></xsl:otherwise>
</xsl:choose>
</xsl:variable>
However, in XSLT 1.0, this will convert the output of the chosen result to a result tree fragment (RTF: basically, a frozen XML subtree). After that, you won't be able to use any significant XPath expressions on $firstcall to select things from it. If you need to do XPath selections on $firstcall later, e.g. select="$firstcall[1]", you then have a few options...
Put those selections into the <xsl:when> or <xsl:otherwise> so that they happen before the data gets converted to an RTF. Or,
Consider the node-set() extension, which converts an RTF to a nodeset, so you can do normal XPath selections from it. This extension is available in most XSLT processors but not all. Or,
Consider using XSLT 2.0, where RTFs are not an issue at all. In fact, in XPath 2.0 you can put normal if/then/else conditionals inside the XPath expression if you want to.
Implement it in XPath 1.0, using nested predicates like
:
select="$topcall[$topcall] |
($focusedcall[$focusedcall] | $thiscall[not($focusedcall)])[not($topcall)]"
and keep on nesting as deep as necessary. In other words, here I took the XPath expression for 2 alternatives above, and replaced $focusedcall with
($focusedcall[$focusedcall] | $thiscall[not($focusedcall)])
The next iteration, you would replace $thiscall with
($thiscall[$thiscall] | $thatcall[not($thiscall)])
etc.
Of course this becomes hard to read, and error-prone, so I would not choose this option unless the others aren't feasible.
Does <xsl:variable name="firstcall" select="($topcall | $focusedcall)[1]"/> do what you want? That is usually the way to take the first node in document order of different types of nodes.
I. XSLT 1.0 Solution This short (30 lines), simple and parameterized transformation works with any number of node types/names:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pRatedCalls">
<call type="topcall"/>
<call type="focusedcall"/>
<call type="normalcall"/>
</xsl:param>
<xsl:variable name="vRatedCalls" select=
"document('')/*/xsl:param[#name='pRatedCalls']/*"/>
<xsl:variable name="vDoc" select="/"/>
<xsl:variable name="vpresentCallNames">
<xsl:for-each select="$vRatedCalls">
<xsl:value-of select=
"name($vDoc//*[name()=current()/#type][1])"/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<xsl:copy-of select=
"//*[name()
=
substring-before(normalize-space($vpresentCallNames),' ')]"/>
</xsl:template>
</xsl:stylesheet>
When applied to this XML document (do note the document order doesn't coincide with the specified priorities in the pRatedCalls parameter):
<t>
<normalcall/>
<focusedcall/>
<topcall/>
</t>
produces exactly the wanted, correct result:
<topcall/>
when the same transformation is applied to the following XML document:
<t>
<normalcall/>
<focusedcall/>
</t>
again the wanted and correct result is produced:
<focusedcall/>
Explanation:
The names of the nodes that are to be searched for (as many as needed and in order of priority) are specified by the global (typically externally specified) parameter named $pRatedCalls.
Within the body of the variable $vpresentCallNames we generate a space-separated list of names of elements that are both specified as a value of the type attribute of a call elementin the$pRatedCalls` parameter and also are names of elements in the XML document.
Finally, we determine the first such name in this space-separated list and select all elements in the document, that have this name.
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pRatedCalls" select=
"'topcall', 'focusedcall', 'normalcall'"/>
<xsl:template match="/">
<xsl:sequence select=
"//*
[name()=$pRatedCalls
[. = current()//*/name()]
[1]
]"/>
</xsl:template>
</xsl:stylesheet>
hi all i have written a logic based on a requirement concact more than two data at a time in my xslt code but i m not reaching my expected output can any one give some suggestions
here is my xml
<Swift>
<block4>
<tag>
<name>50K</name>
<value>
0101/0457887750
SAMAROCA
MENENDEZ Y PELAYO
</value>
</tag>
</block4>
</Swift>
i have written an xslt here :
<xsl:template match="swift/message/block4/tag [name='50K']">
<xsl:variable name ="del50k" select ="(translate(substring-after(value,'
'),'
','~'))"/>
<xsl:value-of select="concat(substring(value, 1, 5), ',',substring(substring-before(value,'
'),6), ',',$del50k)" />
</xsl:template>
is that way doing is correct or not ? can any one help
EXPECTED OUTPUT:
0101/,0457887750,SAMAROCA~MENENDEZ Y PELAYO
I'm giving you a full working example based on your input. A few notes:
Use normalize-space() and split the string by space.
Just play with substring-before and substring-after.
make sure to use xsl:strip-space.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output omit-xml-declaration="yes" method="text"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="space" select="' '"/>
<xsl:template match="block4/tag[name='50K']">
<xsl:variable name="value" select="normalize-space(value)"/>
<xsl:variable name="code" select="substring-before($value,$space)"/>
<xsl:variable name="string1" select="concat(
substring-before($code,'/'),
'/,',
substring-after($code,'/'))"/>
<xsl:variable name="string2" select="substring-before(
substring-after($value,$space),
$space)"/>
<xsl:variable name="string3" select="substring-after(
substring-after($value,$space),
$space)"/>
<xsl:value-of select="concat($string1,',',$string2,'~',$string3)"/>
</xsl:template>
<xsl:template match="name|value"/>
</xsl:stylesheet>
Your biggest problem is that value is you context node (defined in your template's match attribute), but you're referring to value in your XPath. This will look for a value node within the value node, which is obviously wrong.
In your <xsl:variable> and <xsl:value-of> statements, change refences to value to ., to refer to the current node instead.
I think that's probably not the only issue, but given that your template isn't going to match anything in that document anyway, it's difficult to derive where else it could be going wrong. One possible additional problem is that your substring-before(value,'
') predicate within your <xsl:value-of> isn't going to return anything with the formatting given, as there's a newline before the 0101/etc... Now I think about it, that's also going to be issue in the substring-after in the previous line. That's very dependent on how it's actually formatted though, but from what you've given here, it is a problem.