Designating columns based on element order in XML
My XML is shown below:
<?xml version="1.0" encoding="UTF-8"?>
<JobListing>
<ColumnOrder>
<columnId>4</columnId>
<columnId>2</columnId>
<columnId>5</columnId>
<columnId>1</columnId>
<columnId>3</columnId>
</ColumnOrder>
<Jobs>
<Job>
<title>Java Developer</title>
<code>JAVA</code>
<openings>10</openings>
<location>USA</location>
<duration>6 months</duration>
</Job>
<Job>
<title>.NET Developer</title>
<code>DOTNET</code>
<openings>10</openings>
<location>USA</location>
<duration>6 months</duration>
</Job>
</Jobs>
</JobListing>
I've a specific requirement that Jobs should be listed in a HTML page based on ColumnOrder
specified in the XML. Internally, each columnId
is mapped to a column as given below:
- 1 -> Title
- 2 -> Code
- 3 -> Openings
- 4 -> Location
- 5 -> Duration
In this case, for example, Job listing page should list columns in this order - Location, Code, Duration, Title, Openings
. Am expecting something like as explained below:
<tr>
loop jobs
for each columnorder
if(columnId == 1)
<td>get Title</td>
else if (columnId == 2)
<td>get Code</td>
else if (columnId == 3)
<td>get Openings</td>
else if (columnId == 4)
<td>get Location</td>
开发者_开发技巧 else if (columnId == 5)
<td>get Duration</td>
end if
end columnorder
end jobs
</tr>
How do I achieve this using XSLT?
Any help or different ideas/suggestions would be greatly appreciated.
EDIT: The <Job>
elements in the XML (title, openings, location, duration, code) are not necessarily in the same order. In fact, in our current framework, it gets generated in ascending order (like code, duration, location, openings, title). How to make it work without taking into account order of elements of <Job>
?
This transformation works even when the children of Job
are mixed in any order:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" exclude-result-prefixes="my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:Mapping>
<id val="1">title</id>
<id val="2">code</id>
<id val="3">openings</id>
<id val="4">location</id>
<id val="5">duration</id>
</my:Mapping>
<xsl:variable name="vOrder" select="/*/ColumnOrder/*"/>
<xsl:variable name="vMap" select=
"document('')/*/my:Mapping/*"/>
<xsl:template match="Jobs">
<table>
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="Job">
<tr>
<xsl:apply-templates select="*">
<xsl:sort data-type="number" select=
"count($vOrder
[. = $vMap
[. = name(current())]/@val
]
/preceding-sibling::*
)
"/>
</xsl:apply-templates>
</tr>
</xsl:template>
<xsl:template match="Job/*">
<td><xsl:value-of select="."/></td>
</xsl:template>
<xsl:template match="ColumnOrder"/>
</xsl:stylesheet>
When applied on the provided XML document:
<JobListing>
<ColumnOrder>
<columnId>4</columnId>
<columnId>2</columnId>
<columnId>5</columnId>
<columnId>1</columnId>
<columnId>3</columnId>
</ColumnOrder>
<Jobs>
<Job>
<title>Java Developer</title>
<code>JAVA</code>
<openings>10</openings>
<location>USA</location>
<duration>6 months</duration>
</Job>
<Job>
<title>.NET Developer</title>
<code>DOTNET</code>
<openings>10</openings>
<location>USA</location>
<duration>6 months</duration>
</Job>
</Jobs>
</JobListing>
The wanted, correct result is produced:
<table>
<tr>
<td>USA</td>
<td>JAVA</td>
<td>6 months</td>
<td>Java Developer</td>
<td>10</td>
</tr>
<tr>
<td>USA</td>
<td>DOTNET</td>
<td>6 months</td>
<td>.NET Developer</td>
<td>10</td>
</tr>
</table>
This stylesheet:
<?xml version="1.0"?>
<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="JobListing">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="Jobs">
<table>
<xsl:apply-templates />
</table>
</xsl:template>
<xsl:template match="Job">
<tr>
<xsl:variable name="currentJob" select="."/>
<xsl:for-each select="/JobListing/ColumnOrder/columnId">
<xsl:choose>
<xsl:when test="current()=1">
<xsl:apply-templates select="$currentJob/title" />
</xsl:when>
<xsl:when test="current()=2">
<xsl:apply-templates select="$currentJob/code" />
</xsl:when>
<xsl:when test="current()=3">
<xsl:apply-templates select="$currentJob/openings" />
</xsl:when>
<xsl:when test="current()=4">
<xsl:apply-templates select="$currentJob/location" />
</xsl:when>
<xsl:when test="current()=5">
<xsl:apply-templates select="$currentJob/duration" />
</xsl:when>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:template>
<xsl:template match="Job/*">
<td>
<xsl:apply-templates />
</td>
</xsl:template>
<!--Don't render ColumnOrder-->
<xsl:template match="ColumnOrder"/>
</xsl:stylesheet>
Applied to the sample input produces the following output:
<?xml version="1.0" encoding="UTF-8"?>
<table>
<tr>
<td>USA</td>
<td>JAVA</td>
<td>6 months</td>
<td>Java Developer</td>
<td>10</td>
</tr>
<tr>
<td>USA</td>
<td>DOTNET</td>
<td>6 months</td>
<td>.NET Developer</td>
<td>10</td>
</tr>
</table>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="//Job"/>
</xsl:template>
<xsl:template match="Job">
<xsl:copy>
<xsl:apply-templates select="../../ColumnOrder/columnId">
<xsl:with-param name="pos" select="position()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="columnId">
<xsl:param name="pos"/>
<xsl:copy-of select="../../Jobs/Job[position() = $pos]/*[position() = current()/.]"/>
</xsl:template>
</xsl:stylesheet>
Output:
<Job>
<location>USA</location>
<code>JAVA</code>
<duration>6 months</duration>
<title>Java Developer</title>
<openings>10</openings>
</Job>
<Job>
<location>USA</location>
<code>DOTNET</code>
<duration>6 months</duration>
<title>.NET Developer</title>
<openings>10</openings>
</Job>
精彩评论