How to detect a leading space in the text - xslt

I want to add some strings in the text right after a leading space. Any ideas how to detect the leading space? Thanks.
For example, I would like to add "def" in front of abc but after the leading space.
<AAA>
<CCC> abc</CCC>
</AAA>
Output should become: " defabc"

This 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="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()[starts-with(.,' ')]">
<xsl:value-of select=
"concat(' ', 'def', substring(.,2))"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<AAA>
<CCC> abc</CCC>
</AAA>
produces the wanted, correct result:
<AAA>
<CCC> defabc</CCC>
</AAA>

Assuming, from your tag, that you are trying to do this in xslt, I'd use XSLT's starts-with function.
If you provide some example XSLT code, it'd be easier to explain more.

Besides Dimitre's answer with proper use of pattern matching, this XPath expression could help you:
concat(substring($AddString, 1 div starts-with($String,' ')), $String)

Related

how to change a text of element in a parent tag using xslt

I want to replace the element tag name using xslt. I have an output like this:
<gl-cor:documentInfo>
<gl-cor:entriesType contextRef="journal_context">DocumentID</gl-cor:entriesType>
<gl-cor:uniqueID contextRef="journal_context">RevisionID</gl-cor:uniqueID>
</gl-cor:documentInfo>
<gl-cor:entityInformation>
<gl-cor:entityPhoneNumber>
<gl-cor:phoneNumber contextRef="journal_context">779633</gl-cor:phoneNumber>
</gl-cor:entityPhoneNumber>
<gl-cor:entityFaxNumberStructure>
<gl-cor:entityFaxNumbercontextRef="journal_context">1234-56-89</gl-cor:entityFaxNumber>
</gl-cor:entityFaxNumberStructure>
</gl-cor:entityInformation>
And, I want my output to be looks like this:
<gl-cor:documentInfo>
<gl-cor:entriesType contextRef="journal_context">DocumentID</gl-cor:entriesType>
<gl-bus:uniqueID contextRef="journal_context">RevisionID</gl-cor:uniqueID>
</gl-cor:documentInfo>
<gl-cor:entityInformation>
<gl-bus:entityPhoneNumber>
<gl-bus:phoneNumber contextRef="journal_context">779633</gl-bus:phoneNumber>
</gl-bus:entityPhoneNumber>
<gl-bus:entityFaxNumberStructure>
<gl-bus:entityFaxNumbercontextRef="journal_context">1234-56-89</gl-bus:entityFaxNumber>
</gl-bus:entityFaxNumberStructure>
</gl-cor:entityInformation>
All the children of <gl-cor:entityInformation> should replace instead of gl-cor, it should be gl-bus. Is it possible to do this?
I tried to create a sample xslt but it didn't work. The error occurs in the <gl-bus:phoneNumber>, because I think it is contain a special characters? like "-" and ":".
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="gl-cor:entityInformation/gl-cor:entityPhoneNumber/gl-cor:phoneNumber">
<gl-bus:phoneNumber>
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</gl-bus:phoneNumber>
</xsl:template>
Can someone help me solve this problem? Thanks alot.
First of all gl-cor and gl-bus are namespace prefixes. Namespace prefixes are written before an XML element and seperated from the XML element with a :. So your problem is not because of the characters - and :, these are all valid characters, please also read these articles/tutorials:
http://www.w3schools.com/xml/xml_namespaces.asp
http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Predefined_entities_in_XML
To answer your problem we need to know what the namspace URIs are for gl-cor and gl-bus, but it should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:gl-cor="http://example.org/gl-cor" xmlns:gl-bus="http://example.org/gl-bus">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[ancestor::gl-cor:entityInformation]">
<xsl:element name="gl-bus:{local-name()}">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The template *[ancestor::gl-cor:entityInformation] will match on all (grand)children of gl-cor:entityInformation.
NOTE
The namespaces in the XSLT should be updated and match to your input XML:
xmlns:gl-cor="http://example.org/gl-cor"
xmlns:gl-bus="http://example.org/gl-bus"

Replacing particuar String using xsl

I have a block as below.
<rightOperand>.*ifIndedx.*</rightOperand>
But i need to change the above snippet to the below one
<rightOperand>(?i)(?s).*ifIndex.*</rightOperand>
This translation needs to be done only when the right operand starts and ends with the string .*
please provide me some pointers .
You can do this my overriding the identity transform with an extra template just to match the text within rightOperand that matches your criteria
<xsl:template match="rightOperand/text()
[starts-with(., '.*')]
[substring(., string-length(.) - 1, 2) = '.*']">
Note that XSLT 1.0 does not have the ends-with function, which is why there is the extra work to check the ending with substring. If you were using XSLT 2.0 you could simplify this with ends_with though.
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="rightOperand/text()
[starts-with(., '.*')]
[substring(., string-length(.) - 1, 2) = '.*']">
<xsl:text>(?i)(?s)</xsl:text><xsl:copy />
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output:
<rightOperand>(?i)(?s).*ifIndedx.*</rightOperand>

XSL: How to concatenate nodes with conditions?

I have the following code (eg):
<response>
<parameter>
<cottage>
<cot>
<res>
<hab desc="Lakeside">
<reg cod="OB" prr="600.84>
<lwz>TR#2#AB#200.26#0#QB#OK#20120829#20120830#EU#3-0#</lwz>
<lwz>TR#2#AB#200.26#0#QB#OK#20120830#20120831#EU#3-0#</lwz>
<lwz>TR#2#AB#200.26#0#QB#OK#20120831#20120901#EU#3-0#</lwz>
I need to create a concatenated string that includes the whole of the first 'lwz' line and then the price (200.26, but it can be different in each line) for each corresponding line.
So the output, separating each line with | would be:
TR#2#AB#200.26#0#QB#OK#20120829#20120830#EU#3-0#|200.26|200.26
Thanks
This XSLT 1.0 transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="lwz[1]">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="lwz[position() >1]">
<xsl:value-of select=
"concat('
',
substring-before(substring-after(substring-after(substring-after(.,'#'),'#'),'#'),'#')
)
"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the provided text (converted to a well-formed XML document !!!):
<response>
<parameter>
<cottage>
<cot>
<res>
<hab desc="Lakeside">
<reg cod="OB" prr="600.84">
<lwz>TR#2#AB#200.26#0#QB#OK#20120829#20120830#EU#3-0#</lwz>
<lwz>TR#2#AB#200.26#0#QB#OK#20120830#20120831#EU#3-0#</lwz>
<lwz>TR#2#AB#200.26#0#QB#OK#20120831#20120901#EU#3-0#</lwz>
</reg>
</hab>
</res>
</cot>
</cottage>
</parameter>
</response>
produces the wanted, correct result:
TR#2#AB#200.26#0#QB#OK#20120829#20120830#EU#3-0#
200.26
200.26
II XSLT 2.0 solution:
This transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="lwz[1]">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="lwz[position() >1]">
<xsl:value-of select=
"concat('
', tokenize(.,'#')[4])"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the above XML document, again produces the wanted, correct result. Note the use of the standard XPath 2.0 function tokenize():
TR#2#AB#200.26#0#QB#OK#20120829#20120830#EU#3-0#
200.26
200.26
You can use the XPath substring function to select substrings from your lwz node data. You don't really give much more detail about your problem, if you want a more detailed answer, perhaps provide the full XML document and your best-guess XSLT

I want to select all texts (including node names) under a node

I currently have a xml file like this:
<aaa>
<b>I am a <i>boy</i></b>.
</aaa>
How can I get the exact string as: <b>I am a <i>boy</i></b>.? Thanks.
You have to tell XSLT that you want to copy elements through as well. That can be done with an additional rule. Note that I use custom select clauses on my apply-templates elements to select attributes as well as all node-type objects. Also note that the rule for aaa takes precedence, and does not copy the aaa element itself to the output.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="aaa">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
<aaa>
<b>I am a <i>boy</i></b>.
</aaa>
How can I get the exact string as:
<b>I am a <i>boy</i></b>.?
The easiest/shortest way to do this in your case is to output the result of the following XPath expression:
/*/node()
This means: "Select all nodes that are children of the top element."
Of course, there are some white-space-only text nodes that we don't want selected, but XSLT can take care of this, so the XPath expression is just as simple as shown above.
Now, to get the result with an XSLT transformation, we use the following:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy-of select="/*/node()"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document, the wanted result is produced:
<b>I am a <i>boy</i></b>.
Do note:
The use of the <xsl:copy-of> xslt instruction (not <xsl:value-of>), which copies nodes, not string values.
The use of the <xsl:strip-space elements="*"/> XSLT instruction, directing the XSLT processor to ignore any white-space-only text node in the XML document.

XSLT: remove all but the first occurrence of a given node

I have XML something like this:
<MyXml>
<RandomNode1>
<TheNode>
<a/>
<b/>
<c/>
</TheNode>
</RandomeNode1>
<RandomNode2>
</RandomNode2>
<RandomNode3>
<RandomNode4>
<TheNode>
<a/>
<b/>
<c/>
</TheNode>
</RandomNode4>
</RandomNode3>
</MyXml>
Where <TheNode> appears throughout the XML but not at the same level, often deep within other nodes. What I need to do is eliminate all occurrences of <TheNode> EXCEPT the first. The rest are redundant and taking up space. What would be the XSL that could do this?
I have something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<xsl:template match="//TheNode[position()!=1]">
</xsl:template>
</xsl:stylesheet>
But that is not correct. Any suggestions?
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="TheNode[preceding::TheNode]"/>
</xsl:stylesheet>
//TheNode[position()!=1] does not work because here, position() is relative to the parent context of each <TheNode>. It would select all <TheNode>s which are not first within their respective parent.
But you were on the right track. What you meant was:
(//TheNode)[position()!=1]
Note the parentheses - they cause the predicate to be applied to the entire selected node-set, instead of to each node individually.
Unfortunately, even though this is valid XPath expression, it is not valid as a match pattern. A match pattern must be meaningful (applicable) to an individual node, it cannot be a select expression.
So #Alohci's solution,
//TheNode[preceding::TheNode]
is the correct way to express what you want.
Other approach for the pattern would be:
<xsl:template match="TheNode[generate-id()
!= generate-id(/descendant::TheNode[1)]"/>
Note: It's more likely that an absolute expression gets optimizated inteads of a relative expression like preceding::TheNode