开发者

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>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜