XSLT to create node only field exist - xslt

I have a requirement where i need to consider node (Node name : LineItem) only when one of the its element (sequenceNumber) exists, remaining all the LineItems we need to remove.
I tried below, but its not working.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="LineItem[sequenceNumber]"/>
</xsl:stylesheet>``

Instead of <xsl:template match="LineItem[sequenceNumber]"/> I think you want <xsl:template match="LineItem[not(sequenceNumber)]"/> or, based on your comment, if the other element can occur as a descendant, you need <xsl:template match="LineItem[not(.//sequenceNumber)]"/>.

try with xsl:choose
<xsl:template match="LineItem">
<xsl:choose>
<xsl:when test=".//sequenceNumber">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise>
<!-- skip element -->
</xsl:otherwise>
<xsl:choose>
</xsl:template>

Related

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".

Wrap an ordered list of nodes

Hi I have an xml document which look like that:
<a> <!-- Several nodes "a" with the same structure for children -->
<b>12</b>
<c>12</c>
<d>12</d>
<e>12</e>
<f>12</f>
<g>12</g>
</a>
I'm trying to obtain the following document using xslt 2.0
<a>
<b>12</b>
<c>12</c>
<wrap>
<d>12</d>
<e>12</e>
<f>12</f>
<g>12</g>
</wrap>
</a>
I started my xsl file with
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
And changed it for several cases like replacing a string part, filter some nodes, etc.
But I'm stuck with "selecting four consecutive nodes", any clue on how to achieve the wrapping?
If all your a elements are genuinely exactly the same structure then the simplest would be a brute-force
<xsl:template match="a">
<xsl:copy>
<xsl:apply-templates select="b | c" />
<wrap>
<xsl:apply-templates select="d | e | f | g" />
</wrap>
</xsl:copy>
</xsl:template>
or if you want to be a bit cleverer
<wrap>
<xsl:apply-templates select="* except (b | c)" />
</wrap>
If you want to always "wrap" the last four child elements of a, then how about
<xsl:template match="a">
<xsl:variable name="lastFour" select="*[position() > (last() - 4)]" />
<xsl:copy>
<xsl:apply-templates select="* except $lastFour" />
<wrap>
<xsl:apply-templates select="$lastFour" />
</wrap>
</xsl:copy>
</xsl:template>
With XSLT 2.0 you can also make use of for-each-group group-adjacent:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a">
<xsl:copy>
<xsl:for-each-group select="*" group-adjacent="boolean(self::d | self::e | self::f | self::g)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<wrap>
<xsl:apply-templates select="current-group()"/>
</wrap>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT - Best practice for moving specific attributes around

I was looking for some guidance on how best to approach my issue.
I have an XML document like the following but on a larger scale.
<NewDataSet>
<Table Attri1="Attri1Val" Attri2="Attri2Val" Attri3="Attri3Val" Attri4="Attri4Val" Attri5="Attri5Val" Attri6="Attri6Val" Attri7="Attri7" />
</NewDataSet>
I need to move certain attributes from the Table node, for example Attri2 and Attri5 into elements within the Table node, however I need to leave the rest of the attributes as they are.
What would be the best way to approach this? The data scale is about 3-4 times that shown.
EDIT:
Expected output:
<NewDataSet>
<Table Attri1="Attri1Val" Attri3="Attri3Val" Attri4="Attri4Val" Attri6="Attri6Val" Attri7="Attri7">
<Attri2>Attri2Val</Attri2>
<Attri5>Attri5Val</Attri5>
</Table>
</NewDataSet>
The complexity is not really the issue, more the scale of the data and what is the best way to deal with it.
Use
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Table">
<xsl:copy>
<xsl:apply-templates select="#*[not(name() = 'Attri2') and not(name() = 'Attri5')]"/>
<xsl:apply-templates select="#Attri2 | #Attri5 | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Table/#Attri2 | Table/#Attri5">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
[edit]
The name comparison of the attributes is a bit ugly but will probably do for your sample. What we really need is #* execpt (#Attri2, #Attri5), only that is XPath 2.0. With XPath 1.0 the equivalent is
<xsl:template match="Table">
<xsl:copy>
<xsl:variable name="all-attributes" select="#*"/>
<xsl:variable name="to-be-transformed" select="#Attri2 | #Attri5"/>
<xsl:apply-templates select="$all-attributes[count(. | $to-be-transformed) != count($to-be-transformed)]"/>
<xsl:apply-templates select="$to-be-transformed | node()"/>
</xsl:copy>
</xsl:template>
This generic transformation can handle any set of attributes, with any length, whose names can be specified externally to the 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:param name="pToTransform" select="'|Attri2|Attri5|'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Table">
<xsl:copy>
<xsl:apply-templates select=
"#*[not(contains($pToTransform, concat('|',name(),'|')))] | node()"/>
<xsl:apply-templates mode="makeElement"
select="#*[contains($pToTransform, concat('|',name(),'|'))]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*" mode="makeElement">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the provided XML document:
<NewDataSet>
<Table Attri1="Attri1Val" Attri2="Attri2Val"
Attri3="Attri3Val" Attri4="Attri4Val"
Attri5="Attri5Val" Attri6="Attri6Val"
Attri7="Attri7" />
</NewDataSet>
the wanted, correct result is produced:
<NewDataSet>
<Table Attri1="Attri1Val" Attri3="Attri3Val" Attri4="Attri4Val" Attri6="Attri6Val" Attri7="Attri7">
<Attri2>Attri2Val</Attri2>
<Attri5>Attri5Val</Attri5>
</Table>
</NewDataSet>

Replacing the namespace in input xml

I have two types of input xml, one with namespace prefix and another one without prefix and i want to replace the namespaces.
Sample1
<v1:Library xmlns:v1="http://testlibrary" xmlns:v2="http://commonprice">
<v1:Books_details>
<v1:Name>test1</v1:Name>
<v1:title>test2</v1:title>
<v2:price xmlns="http://commonprice">12</v2:price>
</v1:Books_details>
</v1:Library>
Sample2
<Library xmlns="http://testlibrary">
<Books_details>
<Name>test1</Name>
<title>test2</title>
<price xmlns="http://commonprice">12</price>
</Books_details>
</Library>
I have written following XSLT to change the namespace from "http://testlibrary" to "http://newlibrary" and it works fine for sample1 but it doesn't work for the sample2. It gives wrong result. It also the change the namespace of the price element even though it doesn't have namespace to be replaced.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:param name="old_namespace"/>
<xsl:param name="new_namespace"/>
<xsl:template match="/">
<xsl:apply-templates select="#* | node()"/>
</xsl:template>
<xsl:template match="text() | comment() | processing-instruction()">
<xsl:copy>
<xsl:apply-templates select="text() | comment() | processing-instruction()"/>
</xsl:copy>
</xsl:template>
<!-- Template used to copy elements -->
<xsl:template match="*">
<xsl:variable name="name">
<xsl:choose>
<xsl:when test="contains(name(), ':')">
<xsl:value-of select="name()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="local-name()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{$name}" namespace="{$new_namespace}">
<!-- Copy all namespace through except for namespace to be changed -->
<xsl:for-each select="namespace::*">
<xsl:if test="string(.)!=$old_namespace">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Note: My first answer was wrong because in XSLT 1.0, you can't use variable references within a match pattern.
This style-sheet will take either Sample1 or Sample2 as input document and replace all occurrences of the old namespace, from the element names, with the new namespace. Note: you can change the xsl:variable for xsl:param.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="old_namespace" select="'http://testlibrary'" />
<xsl:variable name="new_namespace" select="'http://newlibrary'" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:choose>
<xsl:when test="namespace-uri()=$old_namespace">
<xsl:element name="{local-name()}" namespace="{$new_namespace}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Caveat
The above style-sheet will only change the namespace of the elements. It will not change the namespaces of attributes, nor will it remove extraneous namespaces nodes.
On a more specific solution
It is very unusual to require a general solution for an operation on a variable namespace. Namespaces, being what they are tend to be fixed and known. Consider carefully, if you really need a generalized solution. If you need a specific solution, meaning replacing occurrences of a specific namespace, then things get a lot easier, such as with this style-sheet...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:old="http://testlibrary"
xmlns:new="http://newlibrary">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="old:*">
<xsl:element name="{local-name()}" namespace="http://newlibrary">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
UPDATE
I just noticed Dimitre's solution to a very similar question here.

Why don't copy this dokument attributes from the source xml file?

i'm working the first time with xslt and i really don't understand why this xsl don't copy attributes from the source xml. Perhaps someone can give me a hint??
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="rpl" select="document('ParamInvoice.xml')"/>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="* | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="vInvoiceElement" select="$rpl/StoraInvoice/*[name()=name(current())]"/>
<xsl:copy>
<xsl:if test="$vInvoiceElement/Attribute">
<xsl:call-template name="AttributeErzeugen">
<xsl:with-param name="pAttr" select="$vInvoiceElement/Attribute"/>
</xsl:call-template>
</xsl:if>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template name="AttributeErzeugen">
<xsl:param name="pAttr"/>
<xsl:for-each select="$pAttr">
<xsl:attribute name="{#name}"><xsl:value-of select="."/></xsl:attribute>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Instead of <xsl:copy>, you should use <xsl:copy-of>. The difference between both is that copy copies the element only (without attributes and child elements) and copy-of copies the entire elements (attributes, childs, etc).
Check http://www.w3schools.com/xsl/xsl_w3celementref.asp