How check document is available in xsl? - xslt

How i check select="document('02.xml')/*/Person"/ and select="document('04.xml')/*/Person"/ is available in xsl. when xsl is running
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/xmlResponse">
<xsl:copy>
<xsl:apply-templates select="document('02.xml')/*/Person"/>
<xsl:apply-templates select="document('04.xml')/*/Person"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT processor dependent.
Check first the parser
<xsl:variable name="vendor" select="system-property('xsl:vendor')" />
and then make a choose.
Microsoft use embedded javascript custom function (http://dev.ektron.com/kb_article.aspx?id=482), use FileSystemObject and returns 1 or 0 that can be then tested in XSLT.
Saxon,Xalan:
<xsl:variable name="d03" select="document('03.xml')"/>
<xsl:choose>
<xsl:when test="$d03">
<xsl:apply-templates select="document('03.xml')/*/Person"/>
</xsl:when>
<xsl:otherwise>
<Person name="Matthew" missing="true"/>
</xsl:otherwise></xsl:choose>

Related

XSLT. How to do 2 changes for 1 element? Ordering and renaming

I need to change order of elements. But also rename elements into elements.
So,
I have xml:
<transactionality>
<rollbackexttransactionid>
<rollbackvalidity>3600</rollbackvalidity>
<rollbackscheduledattemps>3</rollbackscheduledattemps>
<manualcorrectionpath>TransactionalityLogs</manualcorrectionpath>
</rollbackexttransactionid>
<transactionalitylogspath>TransactionalityLogs</transactionalitylogspath>
<rollbacklifecycleevents>
<rollbacklifecycle>1200</rollbacklifecycle>
<rollbackscheduledattemps>2</rollbackscheduledattemps>
<manualcorrectionpath>TransactionalityLogs</manualcorrectionpath>
</rollbacklifecycleevents>
<rollbackpayment>
<rollbackvalidity>1200</rollbackvalidity>
<rollbackscheduledattemps>2</rollbackscheduledattemps>
<manualcorrectionpath>TransactionalityLogs</manualcorrectionpath>
</rollbackpayment>
</transactionality>
I need to do element transactionalitylogspath as first in to transactionality.
Rename all elements "rollbackscheduledattemps" to "rollbackscheduledattempts"
Rename rollbacklifecycleevents/rollbacklifecycle to rollbacklifecycleevents/rollbackvalidity
I would'like to have:
<transactionality>
<transactionalitylogspath>TransactionalityLogs</transactionalitylogspath>
<rollbackexttransactionid>
<rollbackvalidity>3600</rollbackvalidity>
<rollbackscheduledattempts>3</rollbackscheduledattempts>
<manualcorrectionpath>TransactionalityLogs</manualcorrectionpath>
</rollbackexttransactionid>
<rollbacklifecycleevents>
<rollbackvalidity>1200</rollbackvalidity>
<rollbackscheduledattempts>2</rollbackscheduledattempts>
<manualcorrectionpath>TransactionalityLogs</manualcorrectionpath>
</rollbacklifecycleevents>
<rollbackpayment>
<rollbackvalidity>1200</rollbackvalidity>
<rollbackscheduledattempts>2</rollbackscheduledattempts>
<manualcorrectionpath>TransactionalityLogs</manualcorrectionpath>
</rollbackpayment>
</transactionality>
I did:
<xsl:template match="transactionality">
<xsl:variable name="elements-after" select="rollbackexttransactionid|rollbacklifecycleevents|rollbackpayment"/>
<xsl:copy>
<xsl:copy-of select="transactionalitylogspath"/>
<xsl:copy-of select="$elements-after">
</xsl:copy-of >
</xsl:copy>
</xsl:template>
<xsl:template match="rollbackscheduledattemps">
<rollbackscheduledattempts>
<xsl:apply-templates select="#* | node()"/>
</rollbackscheduledattempts>
</xsl:template>
byt it doesn't work :(.
Help me please.
Here's how I would do it :
<?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" indent="yes"/>
<xsl:template match="transactionality">
<xsl:copy>
<xsl:apply-templates select="transactionalitylogspath"/>
<xsl:apply-templates select="*[local-name() != 'transactionalitylogspath']"/>
</xsl:copy>
</xsl:template>
<xsl:template match="rollbackscheduledattemps">
<rollbackscheduledattempts>
<xsl:value-of select="."/>
</rollbackscheduledattempts>
</xsl:template>
<xsl:template match="rollbacklifecycleevents/rollbacklifecycle">
<rollbackvalidity>
<xsl:value-of select="."/>
</rollbackvalidity>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Here's a working example : https://xsltfiddle.liberty-development.net/6pS26mL

XSLT split elements by splitter-element

I've tried to combine solutions to the similar questions asked here but all of them work in very specific cases. My case is the following: I have an arbitrary xml document which contains some tags, let's say <separator/>. What I want is to split parent elements of these tags like that:
INPUT:
<some_tag some_attr="some_value">
some text
<some_other_tag>some another text</some_other_tag>
<separator/>
some other content
</some_tag>
OUTPUT:
<some_tag some_attr="some_value">
some text
<some_other_tag>some another text</some_other_tag>
</some_tag>
<separator/>
<some_tag some_attr="some_value">
some other content
</some_tag>
Also, I am limited to XSLT 1.0 since Xalan is used in the project
Either use sibling recursion or use a key to find the nodes "belonging" to a separator. Additional care is needed to copy stuff following the last separator:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="sep" match="*[separator]/node()[not(self::separator)]" use="generate-id(following-sibling::separator[1])"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[separator]">
<xsl:apply-templates select="separator" mode="split"/>
<xsl:if test="separator[last()]/following-sibling::node()">
<xsl:copy>
<xsl:apply-templates select="#* | separator[last()]/following-sibling::node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<xsl:template match="separator" mode="split">
<xsl:apply-templates select=".." mode="split">
<xsl:with-param name="separator" select="."/>
</xsl:apply-templates>
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="*" mode="split">
<xsl:param name="separator"/>
<xsl:copy>
<xsl:apply-templates select="#* | key('sep', generate-id($separator))"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/pPqsHTc
Note that if you use Xalan Java then in the Java world this is much easier with Saxon 9 and XSLT 2/3's <xsl:for-each-group select="node()" group-adjacent="boolean(self::separator)"> or group-starting-with="separator".

XSLT convert xml block under a specific node to xml-escaped content of that node

Any ideas of how the following problem can be solved would be highly appreciated.
INPUT:
<p>
<div>
Original<br/>This is the original <b>acid</b>, a hydroxy monocarboxylic <span class="hl1">acid</span>.
</div>
</p>
Desired Output:
<p>
<div>
Original<br/>This is the original <b>acid</b>, a hydroxy monocarboxylic <span class="hl1">acid</span>.
</div>
</p>
Attempt 1:
`<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output omit-xml-declaration="yes" indent="no" encoding="UTF-8"/>
<!--The identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:value-of select="/" disable-output-escaping="no"/>
</xsl:copy>
</xsl:template>
`
Attempt2:
as an alternative, I thought of placing the child elements' content into a CDATA wrapper.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output omit-xml-declaration="yes" indent="no" encoding="UTF-8"/>
<!--The identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:value-of select="/" />
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</xsl:copy>
</xsl:template>
But that does not give me what I want.
Anyone with a better idea? I'm using XSLT 2.0
Here is a suggestion using XSLT 3.0 serialize() as supported by Saxon 9.6 HE, PE and EE:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:apply-templates mode="serialize"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()" mode="serialize">
<xsl:variable name="ser-params">
<output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
<output:omit-xml-declaration value="yes"/>
</output:serialization-parameters>
</xsl:variable>
<xsl:value-of select="serialize(., $ser-params/*)"/>
</xsl:template>
</xsl:stylesheet>
With older version of Saxon 9 you could use the extension function serialize, as shown in http://xsltransform.net/pPqsHTx:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output name="inline" omit-xml-declaration="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="div">
<xsl:copy>
<xsl:apply-templates mode="serialize"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()" mode="serialize">
<xsl:value-of xmlns:saxon="http://saxon.sf.net/" select="saxon:serialize(., 'inline')"/>
</xsl:template>
</xsl:stylesheet>
Your second attempt should work if you change your xsl:value-of to an xsl:copy-of and tweak the select:
<xsl:template match="div">
<xsl:copy>
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:copy-of select="node()" />
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</xsl:copy>
</xsl:template>

Set position in xslt if variable equals

Can you set the position of a particular item in a for-each loop if the value equals something? I tried the below example but it didn't work:
<xsl:choose>
<xsl:when test='name = "Dining"'>
<xsl:value-of select="position()=1"/>
</xsl:when>
<xsl:otherwise>
[Normal position]
</xsl:otherwise>
</xsl:choose>
Dining will always appear at the top of the list and then the list will render as normal.
You haven't provided an example of your input XML, or shown exactly what you want to do with it, so I am guessing a bit. You could try something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="Dining"/>
<xsl:apply-templates select="*[not(self::Dining)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to the following XML:
<root>
<Bathroom />
<Dining />
<Kitchen />
<Bedroom />
</root>
It produces:
<root>
<Dining />
<Bathroom />
<Kitchen />
<Bedroom />
</root>

How to group neighbour-siblings

i have something like this:
<root>
<a>foo</a>
<b>bar</b>
<groupme>foobar</groupme>
<groupme>baz</groupme>
<groupme>42</groupme>
<c>abc</c>
<d>def</d>
<groupme>foo</groupme>
<x>xyz</x>
<groupme>bar</groupme>
<groupme>foo</groupme>
<z>thats it</z>
</root>
now i need all groume's which are direct neighbours to be a single node like:
<root>
<a>foo</a>
<b>bar</b>
<groupme>foobar baz 42</groupme>
<c>abc</c>
<d>def</d>
<groupme>foo</groupme>
<x>xyz</x>
<groupme>bar foo</groupme>
<z>thats it</z>
</root>
also the groupme nodes containing other nodes, i've just leave them to provide a simple example.
the groupme nodes only apear in a specific level, no groupme nodes in others then root.
any help for me?
Such grouping can be achieved with an approach called "sibling recursion", for your problem I would suggest a stylesheet as follows:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/groupme[not(preceding-sibling::*[1][self::groupme])]">
<xsl:copy>
<xsl:apply-templates select="node()"/>
<xsl:apply-templates select="following-sibling::*[1][self::groupme][1]" mode="list"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/groupme[preceding-sibling::*[1][self::groupme]]"/>
<xsl:template match="root/groupme[preceding-sibling::*[1][self::groupme]]" mode="list">
<xsl:text> </xsl:text>
<xsl:apply-templates select="node()"/>
<xsl:apply-templates select="following-sibling::*[1][self::groupme][1]" mode="list"/>
</xsl:template>
</xsl:stylesheet>
When applied to the input
<root>
<a>foo</a>
<b>bar</b>
<groupme>foobar</groupme>
<groupme>baz</groupme>
<groupme>42</groupme>
<c>abc</c>
<d>def</d>
<groupme>foo</groupme>
<x>xyz</x>
<groupme>bar</groupme>
<groupme>foo</groupme>
<z>thats it</z>
</root>
the result is
<root>
<a>foo</a>
<b>bar</b>
<groupme>foobar baz 42</groupme>
<c>abc</c>
<d>def</d>
<groupme>foo</groupme>
<x>xyz</x>
<groupme>bar foo</groupme>
<z>thats it</z>
</root>
As an alternative to the sibling recursion it is also possible to "grab" the following siblings with a key based approach:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:key
name="first"
match="root/groupme[preceding-sibling::*[1][self::groupme]]"
use="generate-id(preceding-sibling::groupme[not(preceding-sibling::*[1][self::groupme])][1])"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/groupme[not(preceding-sibling::*[1][self::groupme])]">
<xsl:copy>
<xsl:apply-templates select="node()"/>
<xsl:apply-templates select="key('first', generate-id())" mode="list"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/groupme[preceding-sibling::*[1][self::groupme]]"/>
<xsl:template match="root/groupme" mode="list">
<xsl:text> </xsl:text>
<xsl:apply-templates select="node()"/>
</xsl:template>
</xsl:stylesheet>