2010-06-08 7 views
1

У меня есть следующие XML-(это упрощается, и большинство атрибутов опущены):XSLT1.0 Rendering последовательность различных элементов, хранящихся в переменной как М х N таблицы

<Document> 
    <Transfer Name="" From="" To=""/> 
    <Transfer Name="" From="" To=""/> 
    <OtherElement/> 
    <OtherElement/> 
    <Flight AirLina="" From="" To=""/> 
    <Flight AirLina="" From="" To=""/> 
    <OtherElement/> 
    <Hotel Name="" Duration=""/> 
    <Hotel Name="" Duration=""/> 
    <OtherElement/> 
    <OtherElement/> 
    <Extras Name="" Price=""/> 
    <Extras Name="" Price=""/> 
    <Extras Name="" Price=""/> 
    <Extras Name="" Price=""/> 
    <Extras Name="" Price=""/> 
    <Extras Name="" Price=""/> 
    <OtherElement/> 
    <OtherElement/> 
</Document> 

У меня есть переменная, содержащий разные элементы:

<xsl:variable name="packageElements" 
select="/Document/Transfer | /Document/Coach | /Document/Flight | /Document/Hotel | /Document/Extras" /> 

Я хотел бы отобразить эти данные в таблице с двумя столбцами. Я использую XSLT1.0 и MSXSL-процессор.

Я пытался это с простым решением я мог думать:

<table> 
    <tbody> 
    <xsl:for-each select="$packageElements[position() mod 2 = 1]"> 
     <tr> 
     <td> 
      <!-- current element --> 
      <xsl:value-of select="local-name()"/> 
     </td> 
     <td> 
      <!-- element following the current in the $packageElements variable --> 
      <!-- Here is where I'm stuck, I can't figure out how to correctly pick it up :(--> 
     </td> 
     </tr> 
    </xsl:for-each> 
    </tbody> 
</table> 

бы очень признателен за любую помощь.

+0

Без отображения вашего XML это сложно сказать. – Tomalak

+0

@Tomalak: извините, добавлена ​​упрощенная структура документов XML. Надеюсь, это имеет смысл. – DashaLuna

+0

Хороший вопрос (+1). См. Мой ответ для полного решения. :) –

ответ

1

Хорошо,

Я объединил идею @Dimitre Novatchev от ответов этой поста и @ Томалак от [XSLT]: Rendering a node sequence as M x N table поста. Мне очень понравилось решение @ Tomalak с переменной $perRow и шаблон <xsl:template name="filler">, чтобы справиться с пустыми ячейками.

С идеей, полученной от @Dimitre Novatchev, я держусь за $trStartPos. Затем я вычисляю $lowerBoundry и $upperBoundry, которые используются для накопления всех элементов, которые должны отображаться в строке. Может быть, более элегантный способ сделать этот расчет - пожалуйста, дайте мне знать, если вы придумаете один, я бы очень признателен!

Преобразование XSLT

<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="*"/> 

<!-- Select only required elements --> 
<xsl:variable name="tableData" 
    select="/nums/A | /nums/B | /nums/C | /nums/D | /nums/E "/> 
<xsl:variable name="perRow" select="2"/> 

<xsl:template match="/"> 
    <table border="1"> 
     <tbody> 
      <xsl:apply-templates 
       select="$tableData[position() mod $perRow = 1]" mode="tr"/> 
     </tbody>    
    </table> 
</xsl:template> 


<xsl:template match="nums/*" mode="tr"> 
    <xsl:variable name="trStartPos" select="position()" /> 
    <xsl:variable name="upperBoundry" select="$trStartPos * $perRow" /> 
    <xsl:variable name="lowerBoundry" select="$upperBoundry - $perRow" /> 

    <tr> 
     <xsl:variable name="tdsData" 
      select="$tableData[(position() &gt; $lowerBoundry) and (position() &lt;= $upperBoundry)]" /> 
     <xsl:apply-templates select="$tdsData" mode="td"/> 
     <!-- fill up the last row - @Tomalak's solution --> 
     <xsl:if test="count($tdsData) &lt; $perRow"> 
      <xsl:call-template name="filler"> 
       <xsl:with-param name="rest" select="$perRow - count($tdsData)" /> 
      </xsl:call-template> 
     </xsl:if> 
    </tr> 
</xsl:template> 


<!-- Templates for specific elements could be easily added with appropriate info to 
    be displayed depending on the element. This one is general just to display 
    elements' name and value --> 
<xsl:template match="nums/*" mode="td"> 
    <td> 
     El. name: <xsl:value-of select="local-name()"/> - 
     El. value: <xsl:value-of select="."/> 
    </td> 
</xsl:template> 

<!-- @Tomalak solution (please read beginning of this answer for reference) --> 
<xsl:template name="filler"> 
    <xsl:param name="rest" select="0" /> 
    <xsl:if test="$rest"> 
     <td>&#160;</td> 
     <xsl:call-template name="filler"> 
      <xsl:with-param name="rest" select="$rest - 1" /> 
     </xsl:call-template> 
    </xsl:if> 
</xsl:template> 
</xsl:stylesheet> 

применяется на следующий XML

<nums> 
    <A>A-01</A> 
    <num>02</num> 
    <num>03</num> 
    <num>04</num> 
    <B>B-05</B> 
    <num>06</num> 
    <num>07</num> 
    <C>C-08</C> 
    <num>09</num> 
    <D>D-10</D> 
    <num>11</num> 
    <num>12</num> 
    <num>13</num> 
    <E>E-14</E> 
    <num>15</num> 
</nums> 

результаты в следующих выходных

<table border="1"> 
    <tbody> 
     <tr> 
      <td> 
       El. name: A - 
       El. value: A-01 
      </td> 
      <td> 
       El. name: B - 
       El. value: B-05 
      </td> 
     </tr> 
     <tr> 
      <td> 
       El. name: C - 
       El. value: C-08 
      </td> 
      <td> 
       El. name: D - 
       El. value: D-10 
      </td> 
     </tr> 
     <tr> 
      <td> 
       El. name: E - 
       El. value: E-14 
      </td> 
      <td> </td> 
     </tr> 
    </tbody> 
</table> 
1

Я думаю, что сложность здесь:

элемента ниже в $ packageElements переменной

Это текущий является узлом в $ packageElements набор узлы с позицией() больше текущего узла. Но какова позиция текущего узла в наборе узлов пакета $ packegeElements?

Проверить this. Dimitre создает выражение, которое является счетчиком для пересечения между предыдущими узлами (в документе) текущего узла и набором узлов.

1

Это преобразование:

<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:variable name="vData" select="/*/*"/> 

<xsl:template match="/"> 
    <table border="1"> 
    <xsl:apply-templates select="$vData[position() mod 2 = 1]"/> 
    </table> 
</xsl:template> 

<xsl:template match="nums/*"> 
    <xsl:variable name="vPos" select="position()"/> 

    <tr> 
    <td><xsl:value-of select="name()"/></td> 
    <td><xsl:value-of select="$vData[position() = 2*$vPos]"/></td> 
    </tr> 
</xsl:template> 
</xsl:stylesheet> 

при нанесении на этом XML-документ:

<nums> 
    <A>01</A> 
    <num>02</num> 
    <B>03</B> 
    <num>04</num> 
    <C>05</C> 
    <num>06</num> 
    <D>07</D> 
    <num>08</num> 
    <E>09</E> 
    <num>010</num> 
</nums> 

производит разыскиваемого, правильный результат:

<table border="1"> 
    <tr> 
     <td>A</td> 
     <td>02</td> 
    </tr> 
    <tr> 
     <td>B</td> 
     <td>04</td> 
    </tr> 
    <tr> 
     <td>C</td> 
     <td>06</td> 
    </tr> 
    <tr> 
     <td>D</td> 
     <td>08</td> 
    </tr> 
    <tr> 
     <td>E</td> 
     <td>010</td> 
    </tr> 
</table> 
+0

Это не обработка (т. Е. Игнорирование) узлов '', которые нежелательны в выходе. Он обрабатывает все узлы '/ */*' как смежные, но на самом деле они не являются. Я признаю, что входной XML довольно субоптимален, чтобы начать с ... – Tomalak

+0

Спасибо за ответ, это был удар для моего решения. @Tomalak: Понял, что этот ответ не учитывает, что я хочу, чтобы выбранные элементы формировали оригинальный XML. Я использовал ваше решение для «[XSLT]: рендеринг последовательности узлов как запись таблицы M x N в http://stackoverflow.com/questions/2355952/xslt-rendering-a-node-sequence-as-mxn -table Очень элегантный, я узнал что-то новое - спасибо очень! – DashaLuna

+0

@Tomalak: Ну, что делать, когда я абсолютно (еще) не понимаю ее XML? Вот почему я предоставил свой XML-документ - чтобы продемонстрировать идею - она, вероятно, может поймать его и сделать все остальное ... –