Reputation: 549
I have an xml as:
<FlightDetails1>
<CouponNumber1>1</CouponNumber1>
<ServiceClass1>Y</ServiceClass1>
</FlightDetails1>
<FlightDetails2>
<CouponNumber2>2</CouponNumber2>
<ServiceClass2>Y</ServiceClass2>
</FlightDetails2>
<FlightDetails3>
<CouponNumber3></CouponNumber3>
<ServiceClass3>N</ServiceClass3>
</FlightDetails3>
Need to transform this xml to the below format:
<FlightDetails>
<CouponNumber>1</CouponNumber>
<ServiceClass>Y</ServiceClass>
</FlightDetails>
<FlightDetails>
<CouponNumber>2</CouponNumber>
<ServiceClass>Y</ServiceClass>
</FlightDetails>
<FlightDetails>
<CouponNumber></CouponNumber>
<ServiceClass>N</ServiceClass>
</FlightDetails>
Previously, when the tags were like <FlightDetails> and <CouponNumber>
, I used the 'copy-of' function in the XSLT. With the tags being renamed, what is the simplest way to achieve that with an xslt?
XSLT:
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select="//FlightDetails1"/>
</xsl:template>
Upvotes: 1
Views: 106
Reputation: 338316
Here's a generic version
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- the identity template -->
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<!-- elements whose names end with digits -->
<xsl:template match="*[
starts-with(name(), translate(name(), '01234567890', ''))
and substring-after(name(), translate(name(), '01234567890', ''))
]">
<xsl:element name="{translate(name(), '01234567890', '')}">
<xsl:apply-templates select="node() | @*" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The second template matches all elements that end in digits by...
removing all digits from the name
translate(name(), '01234567890', '') := 'FlightDetails1' -> 'FlightDetails'
making sure it does not match elements with digits in the middle
starts-with('FlightDetails1', 'FlightDetails') := true
starts-with('Flight1Details', 'FlightDetails') := false
checking there is something after the start of the string (by definition this can only be one or more digits, all other elements already fail the previous test)
substring-after('FlightDetails1', 'FlightDetails') := '1' (evaluates to true)
This way the template matches any element with a name in the form letters123
.
<xsl:element name="{translate(name(), '01234567890', '')}">
then re-creates that element without the trailing digit.
Upvotes: 1
Reputation: 22617
If you do not want the element names to persist, you can no longer use copy
(or copy-of, the way you are using it).
Instead, match these elements in a separate template, for instance like this:
<xsl:template match="*[starts-with(name(),'FlightDetails')]">
<xsl:element name="FlightDetails">
<!--Further processing-->
</xsl:element>
</xsl:template>
This template matches all forms of FlightDetails
elements, that is to say, regardless of the number at the end of the element name. Then, with the xsl:element
instruction you create a new element with the name "FlightDetails".
Then, go on to write similar templates for the other elements you wish to normalize. Note that now apply-templates/
has come into play to leave it to the processor to decide which template to apply next.
A stylesheet that summarizes all this (I added a root
element to your input XMl to make it well-formed):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/root">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*[starts-with(name(),'FlightDetails')]">
<xsl:element name="FlightDetails">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*[starts-with(name(),'CouponNumber')]">
<xsl:element name="CouponNumber">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*[starts-with(name(),'ServiceClass')]">
<xsl:element name="ServiceClass">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
Gives the following output:
<?xml version="1.0" encoding="UTF-8"?>
<FlightDetails>
<CouponNumber>1</CouponNumber>
<ServiceClass>Y</ServiceClass>
</FlightDetails>
<FlightDetails>
<CouponNumber>2</CouponNumber>
<ServiceClass>Y</ServiceClass>
</FlightDetails>
<FlightDetails>
<CouponNumber/>
<ServiceClass>N</ServiceClass>
</FlightDetails>
Upvotes: 0