Reputation: 25
I have the following XML:
<?xml version='1.0' encoding='UTF-8'?>
<order-data>
<addressRecord>
<address>
<resState>CA</resState>
<resStreetnum>1250</resStreetnum>
<resStreetname>CYPRESS ROAD</resStreetname>
<resPOBox/>
<resCity>SAN DIEGO</resCity>
<resAptNo>11B</resAptNo>
<resZip>92601</resZip>
</address>
</addressRecord>
<empAddressRecord>
<address>
<empCity>ALBUQUERQUE</empCity>
<empStreetnum>9874</empStreetnum>
<empStreetname>COYOTE TRAIL</empStreetname>
<empState>NM</empState>
<employer>ACME ROCKET COMPANY</employer>
<empZip>87104</empZip>
</address>
</empAddressRecord>
</order-data>
I use the following to sort and rename address elements for use in a second application (I have no control over the order of the elements in the source xml):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" version="1.0" encoding="UTF-8"/>
<xsl:template match="text()">
<xsl:value-of select="normalize-space(.)"/>
</xsl:template>
<!-- Identity transform -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- Rename element -->
<xsl:template match="order-data">
<order>
<xsl:apply-templates select="@* | node()"/>
</order>
</xsl:template>
<!-- Address Record -->
<xsl:template match="addressRecord/address | AddressRecord/address">
<xsl:element name="Group">
<xsl:apply-templates select="resStreetnum"/>
<xsl:apply-templates select="resStreetname"/>
<xsl:apply-templates select="resAptNo"/>
<xsl:apply-templates select="resPOBox"/>
<xsl:apply-templates select="resCity"/>
<xsl:apply-templates select="resState"/>
<xsl:apply-templates select="resZip"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/resStreetnum">
<xsl:element name="V1">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/resStreetname">
<xsl:element name="V2">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/resAptNo">
<xsl:element name="V3">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/resPOBox">
<xsl:element name="V4">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/resCity">
<xsl:element name="V5">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/resState">
<xsl:element name="V6">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/resZip">
<xsl:element name="V7">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<!-- Employer and Address Record -->
<xsl:template match="empAddressRecord/address">
<xsl:element name="Group">
<xsl:apply-templates select="employer"/>
<xsl:apply-templates select="empStreetnum"/>
<xsl:apply-templates select="empStreetname"/>
<xsl:apply-templates select="empAptNo"/>
<xsl:apply-templates select="empPOBox"/>
<xsl:apply-templates select="empCity"/>
<xsl:apply-templates select="empState"/>
<xsl:apply-templates select="empZip"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/employer">
<xsl:element name="V1">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/empStreetnum">
<xsl:element name="V2">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/empStreetname">
<xsl:element name="V3">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/empAptNo">
<xsl:element name="V4">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/empPOBox">
<xsl:element name="V5">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/empCity">
<xsl:element name="V6">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/empState">
<xsl:element name="V7">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="address/empZip">
<xsl:element name="V8">
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
</xsl:transform>
Which results in:
<?xml version="1.0" encoding="UTF-8"?>
<order>
<addressRecord>
<Group>
<V1>1250</V1>
<V2>CYPRESS ROAD</V2>
<V3>11B</V3>
<V4/>
<V5>SAN DIEGO</V5>
<V6>CA</V6>
<V7>92601</V7>
</Group>
</addressRecord>
<empAddressRecord>
<Group>
<V1>ACME ROCKET COMPANY</V1>
<V2>9874</V2>
<V3>COYOTE TRAIL</V3>
<V6>ALBUQUERQUE</V6>
<V7>NM</V7>
<V8>87104</V8>
</Group>
</empAddressRecord>
</order>
The problem arises when the incoming source xml does not provide empty tags for missing elements (note <empAddressRecord>
does not contain a <V4/>
or <V5/>
tag). The second application does not handle the missing data very well.
I am looking for a XSLT mechanism to create the empty <V1/>
, <V2/>
, etc. tags when the source xml does not provide the empty element tag. I've searched this website as well as others for solutions and have experimented with conditionals and other mechanisms but nothing so far has seemed to work.
Thanks.
Upvotes: 1
Views: 149
Reputation: 117043
Why don't you do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/order-data">
<order>
<xsl:apply-templates/>
</order>
</xsl:template>
<xsl:template match="addressRecord | empAddressRecord">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="addressRecord/address">
<Group>
<V1>
<xsl:value-of select="resStreetnum"/>
</V1>
<V2>
<xsl:value-of select="resStreetname"/>
</V2>
<V3>
<xsl:value-of select="resAptNo"/>
</V3>
<V4>
<xsl:value-of select="resPOBox"/>
</V4>
<V5>
<xsl:value-of select="resCity"/>
</V5>
<V6>
<xsl:value-of select="resState"/>
</V6>
<V7>
<xsl:value-of select="resZip"/>
</V7>
</Group>
</xsl:template>
<xsl:template match="empAddressRecord/address">
<Group>
<V1>
<xsl:value-of select="employer"/>
</V1>
<V2>
<xsl:value-of select="empStreetnum"/>
</V2>
<V3>
<xsl:value-of select="empStreetname"/>
</V3>
<V4>
<xsl:value-of select="empAptNo"/>
</V4>
<V5>
<xsl:value-of select="empPOBox"/>
</V5>
<V6>
<xsl:value-of select="empCity"/>
</V6>
<V7>
<xsl:value-of select="empState"/>
</V7>
<V8>
<xsl:value-of select="empZip"/>
</V8>
</Group>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
Reputation: 167641
If you can move to XSLT 2.0 then you can use an approach like
<xsl:param name="default-elements">
<address>
<employer/>
<empStreenum/>
<empStreetname/>
<empAptNo/>
<empPOBox/>
<empCity/>
<empState/>
<empZip/>
</address>
</xsl:param>
<!-- Employer and Address Record -->
<xsl:template match="empAddressRecord/address">
<Group>
<xsl:apply-templates
select="
(employer, $default-elements/address/employer)[1],
(empStreetnum, $default-elements/address/empStreetnum)[1],
(empStreetname, $default-elements/address/empStreetname)[1],
(empAptNo, $default-elements/address/empAptNo)[1],
(empPOBox, $default-elements/address/empPOBox)[1],
(empCity, $default-elements/address/empCity)[1],
(empState, $default-elements/address/empState)[1],
(empZip, $default-elements/address/empZip)[1]"
/>
</Group>
</xsl:template>
Upvotes: 1