2012-06-07 9 views
0

Я только что познакомился с xslt и xml за последние несколько недель, и мне срочно нужна помощь в написании кода для достижения того, чего я хочу достичь. У меня есть следующий XML:XSLT foreach loop

<?xml version="1.0" encoding="UTF-8"?> 
<abc1 formName="Form"> 
    <Level1> 
     <Element1>ZZZ</Element1> 
     <Element2> 
      <SubElement1>Apples</SubElement1> 
      <SubElement2>Oranges</SubElement2> 
      <SubElement3>Pears</SubElement3> 
      <SubElement4>Blueberries</SubElement4> 
      <SubElement5>Milkshakes</SubElement5> 
     </Element2> 
    </Level1> 
    <Level1> 
     <Element1>XXX</Element1> 
     <Element2> 
      <SubElement1>Apples</SubElement1> 
      <SubElement2>Oranges</SubElement2> 
      <SubElement3>Kiwifruit</SubElement3> 
      <SubElement4>Blueberries</SubElement4> 
      <SubElement5>Soda</SubElement5> 
     </Element2> 
    </Level1> 
</abc1> 

и следующий HTML таблица:

<table> 
<tr> 
    <td width="180" > Row 1</td> 
    <td width="540" colspan="4"> Cell_A</td> 
</tr> 
<tr> 
    <td width="180" >Types</td> 
    <td width="180" >Type 1</td> 
    <td width="180" >Type 2</td> 
    <td width="180" >Type 3</td> 
</tr> 
<tr> 
    <td width="180" >Row 2</td> 
    <td width="180" >Cell_B</td> 
    <td width="180" >Cell_C</td> 
    <td width="180" >Cell_D</td> 
</tr> 
<tr> 
    <td width="180" > Row 3</td> 
    <td width="180" >Cell_E</td> 
    <td width="180" >Cell_F</td> 
    <td width="180" >Cell_G</td> 
</tr> 
<tr> 
    <td width="180" >Row 4</td> 
    <td width="180" >Cell_H</td> 
    <td width="180" >Cell_I</td> 
    <td width="180" >Cell_J</td> 
</tr> 
<tr> 
    <td width="180" > Row 5</td> 
    <td width="540" colspan="4"> Cell_K</td> 
</tr> 
</table> 

У меня возникли проблемы с использованием XSLT для извлечения данных из XML в таблицу, так как правила, что мне нужно применять делают его очень сложным. Ниже приведены правила, которые должны применяться к ячейкам, с которыми у меня возникают проблемы.

(1) Если значение <Element1> одно и то же на протяжении всего XML, то:

Если <Element1> = 'ZZZ', то Cell_B = '10', Cell_C = '20', а Cell_D = '30'

Если <Element1> = 'XXX', то Cell_B = '100', Cell_C = '90', а Cell_D = '80'

, но если значение <Element1> отличается в XML, то:

Cell_B = '10, 100 ', Cell_C = '20, 90' и Cell_D = '30, 80 '

(2) Если значение <SubElement5> одно и то же на протяжении всего XML, то:

Cell_J = значение <SubElement5>

, но если значение <SubElement5> отличается в XML, то:

Cell_J = значение обоих значений <SubElement5>, разделенных запятой

поэтому в этом случае значением для Cell_J будет «Milkshakes, Soda».

Я экспериментировал с различными вещами, используя:

<xsl:for-each select="./Level1/Element2"> 
<xsl:value-of select="./SubElement5"/> 
</xsl:for-each> 

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

Edit: Пожалуйста, обратите внимание, что клетки я, указанные выше (клетки B, C, D и J) являются единственными, я нужна помощь с. Кроме того, для Element1 существует четыре возможных значения: : ZZZ, XXX, AAA и BBB. Требуемые значения для каждого из них будет: (клетки B, C, и D)

ZZZ:10,20,30 
XXX:100,90,80 
AAA:40,30,30 
BBB:50,30,20 

так что, если существует только один потенциальное значение во всем XML, клетки должны появляться со значениями, как указано выше. Если значения двух элементов 1 различны, ячейки должны перечислять приведенные выше значения в каждой ячейке, разделенные запятой.

Что касается cell_j, я попытаюсь объяснить его немного лучше.

Во-первых, мне нужно определить, совпадает ли значение <SubElement5> на всем протяжении xml.В этом случае это не так, в одном разделе это Milkshakes, а в другом - «Сода». Поэтому cell_J должен содержать текст «Milkshakes, Soda».

Если XML выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?> 
    <abc1 formName="Form"> 
     <Level1> 
      <Element1>ZZZ</Element1> 
      <Element2> 
       <SubElement1>Apples</SubElement1> 
       <SubElement2>Oranges</SubElement2> 
       <SubElement3>Pears</SubElement3> 
       <SubElement4>Blueberries</SubElement4> 
       <SubElement5>Milkshakes</SubElement5> 
      </Element2> 
     </Level1> 
     <Level1> 
      <Element1>XXX</Element1> 
      <Element2> 
       <SubElement1>Apples</SubElement1> 
       <SubElement2>Oranges</SubElement2> 
       <SubElement3>Kiwifruit</SubElement3> 
       <SubElement4>Blueberries</SubElement4> 
       <SubElement5>Milkshakes</SubElement5> 
      </Element2> 
     </Level1> 
    </abc1> 

Тогда значение для cell_j будет просто 'аппетитное'.

Заранее благодарим всех, кто может помочь.

ответ на этот вопрос:

Суммировать, что Вуди сделал ниже для дальнейшего использования чьего:

<?xml version="1.0" encoding="ISO-8859-1"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<xsl:template match="/abc1"> 
      <xsl:variable name="elements" select="//Element1[not(preceding::Element1 = .)]"/> 
<table> 
<tr> 
    <td width="180" > Row 1</td> 
    <td width="540" colspan="4"> Cell_A</td> 
</tr> 
<tr> 
    <td width="180" >Types</td> 
    <td width="180" >Type 1</td> 
    <td width="180" >Type 2</td> 
    <td width="180" >Type 3</td> 
</tr> 
<tr> 
    <td width="180" >Row 2</td> 
    <td width="180" > <xsl:for-each select="$elements"> 
      <xsl:if test="position() != 1">,</xsl:if> 
      <xsl:choose> 
       <xsl:when test=". = 'AAA'">40</xsl:when> 
       <xsl:when test=". = 'BBB'">50</xsl:when> 
       <xsl:when test=". = 'XXX'">100</xsl:when> 
       <xsl:when test=". = 'ZZZ'">10</xsl:when> 
      </xsl:choose> 
     </xsl:for-each></td> 
    <td width="180" ><xsl:for-each select="$elements"> 
      <xsl:if test="position() != 1">,</xsl:if> 
      <xsl:choose> 
       <xsl:when test=". = 'AAA'">30</xsl:when> 
       <xsl:when test=". = 'BBB'">30</xsl:when> 
       <xsl:when test=". = 'XXX'">90</xsl:when> 
       <xsl:when test=". = 'ZZZ'">20</xsl:when> 
      </xsl:choose> 
     </xsl:for-each></td> 
    <td width="180" ><xsl:for-each select="$elements"> 
      <xsl:if test="position() != 1">,</xsl:if> 
      <xsl:choose> 
       <xsl:when test=". = 'AAA'">30</xsl:when> 
       <xsl:when test=". = 'BBB'">20</xsl:when> 
       <xsl:when test=". = 'XXX'">80</xsl:when> 
       <xsl:when test=". = 'ZZZ'">30</xsl:when> 
      </xsl:choose> 
     </xsl:for-each></td> 
</tr> 
<tr> 
    <td width="180" > Row 3</td> 
    <td width="180" >Cell_E</td> 
    <td width="180" >Cell_F</td> 
    <td width="180" >Cell_G</td> 
</tr> 
<tr> 
    <td width="180" >Row 4</td> 
    <td width="180" >Cell_H</td> 
    <td width="180" >Cell_I</td> 
    <td width="180" ><xsl:for-each select="//SubElement5[not(preceding::SubElement5/text() = text())]"> 
     <xsl:if test="position() &gt; 1">, </xsl:if> 
     <xsl:value-of select="."/> 
    </xsl:for-each></td> 
</tr> 
<tr> 
    <td width="180" > Row 5</td> 
    <td width="540" colspan="4"> Cell_K</td> 
</tr> 
</table> 
     </xsl:template> 
    </xsl:stylesheet> 

Вуди, благодарственные вас снова. Это было потрясающе.

+0

Вы забыли указать, что входит в ячейки E, F, G, H и I. Пожалуйста, * отредактируйте * вопрос и укажите недостающую информацию. –

ответ

2

Ключ к вашей проблеме состоит в том, что вы не можете повторять данные, чтобы получить то, что хотите, поскольку нужные вам ответы фиксированы, т. Е. Если это произойдет, выведите это, если это, вывести этот другой. Поэтому вам нужно брать каждый раздел за раз.

Есть ли только два уровня Level1?

 <xsl:template match="/abc1"> 
      <table> 
       <tr> 
        <td width="180" >Row 2</td> 
      <xsl:choose> 
       <xsl:when test="not(Level1/Element1='XXX')"> 
        <!-- only ZZZ --> 
         <td width="180" >10</td> 
         <td width="180" >20</td> 
         <td width="180" >30</td> 
       </xsl:when> 
       <xsl:when test="not(Level1/Element1='ZZZ')"> 
        <!-- only YYY --> 
         <td width="180" >100</td> 
         <td width="180" >90</td> 
         <td width="180" >80</td> 
       </xsl:when> 
       <xsl:otherwise> 
        <!-- some combination --> 
         <td width="180" >10,100</td> 
         <td width="180" >20,90</td> 
         <td width="180" >30,80</td> 
       </xsl:otherwise> 
       </xsl:choose> 
       </tr> 
       .. continued 
      </table> 
     </xsl:template> 
    </xsl:stylesheet> 

и так далее в разделе. Если у вас есть несколько строк, и вы хотите, чтобы они были разделены запятыми, вам нужно сделать это в разделе или если вам нужно поместить один список запятой для каждого раздела.

К сожалению, я не мог видеть то, что вы пытаетесь сделать с cell_j и там, кажется, не быть правилом для остальных

Edit: Однако, если у вас есть много пунктов, а чем только 2, и вы должны разделенный запятыми список, вы можете сделать это с помощью XPath, так:

<tr> 
    <td> 
    <xsl:for-each select="//SubElement5[not(preceding::SubElement5/text() = text())]"> 
     <xsl:if test="position() &gt; 1">, </xsl:if> 
     <xsl:value-of select="."/> 
    </xsl:for-each> 
    </td> 
    .. maybe other rows the same 
</tr> 

Редактировать снова:

так что для обновленной вопрос о cell_j, выше приходит с правильным значения для него.

Для вашего обновления в первом разделе можно будет изменить вариант этой же темы, если вы хотите, чтобы все показанные значения (поэтому есть 4 значения, если у вас есть все ваши 4 варианта). К сожалению, как вы фиксированные значения для каждого, и вы должны делать каждый один за другим, что вам нужно будет сделать это в большой части, поэтому в цикле:

<xsl:for-each select="//Element1[not(preceding::Element1/text() = text())]"> 

, который поставит вас в петлю для каждая уникальная запись, а затем элемент выбора, основанный на том, было ли это значение XXX, ZZZ и т. д.

Edit снова

Есть несколько способов сделать первый раздел вы хотите, в то числе рекурсивных функций, внешние документы и с помощью функции набора узлов различного различного XSLT implimentations, но совершенно родовые легко см образом, это слегка многословно, но легко видеть версию (я надеюсь):

<xsl:variable name="elements" select="//Element1[not(preceding::Element1 = .)]"/> 
<table> 
    <tr> 
     <td width="180" >Row 2</td> 
    <td> 
     <xsl:for-each select="$elements"> 
      <xsl:if test="position() != 1">,</xsl:if> 
      <xsl:choose> 
       <xsl:when test=". = 'AAA'">40</xsl:when> 
       <xsl:when test=". = 'BBB'">50</xsl:when> 
       <xsl:when test=". = 'XXX'">100</xsl:when> 
       <xsl:when test=". = 'ZZZ'">10</xsl:when> 
      </xsl:choose> 
     </xsl:for-each> 
    </td> 
    <td> 
     <xsl:for-each select="$elements"> 
      <xsl:if test="position() != 1">,</xsl:if> 
      <xsl:choose> 
       <xsl:when test=". = 'AAA'">30</xsl:when> 
       <xsl:when test=". = 'BBB'">30</xsl:when> 
       <xsl:when test=". = 'XXX'">90</xsl:when> 
       <xsl:when test=". = 'ZZZ'">20</xsl:when> 
      </xsl:choose> 
     </xsl:for-each> 
    </td> 
    <td> 
     <xsl:for-each select="$elements"> 
      <xsl:if test="position() != 1">,</xsl:if> 
      <xsl:choose> 
       <xsl:when test=". = 'AAA'">30</xsl:when> 
       <xsl:when test=". = 'BBB'">20</xsl:when> 
       <xsl:when test=". = 'XXX'">80</xsl:when> 
       <xsl:when test=". = 'ZZZ'">30</xsl:when> 
      </xsl:choose> 
     </xsl:for-each> 
    </td> 
    </tr> 
</table> 

на самом деле - я не уверен, если итерация на переменную ($ элементов в данном случае) является то, что стандартна, может быть саксоном и msxsl, но если это не так, вы можете заменить его на value (// Элемент1 [нет (предшествующий :: Element1 =.)])

+0

Спасибо за помощь. Чтобы ответить на ваши вопросы, я добавлю дополнительную информацию в свой первоначальный пост. –

+0

Еще раз спасибо. Не могли бы вы раскрыть последний раздел с примером? Я не совсем понимаю, что вы имеете в виду. –

+0

Я имею в виду, что, поскольку после того, как вы установили, какие уникальные элементы у вас есть, вы должны поместить их в каждый , вы должны сделать тест 3 раза, один раз для каждого td, так как нет способа сохранить значение и для печати значений. Поэтому, несмотря на то, что вы проводите тест и обнаруживаете, что говорят, что ZZZ присутствует, и вы знаете все 3 значения из всех tds, вы должны завершить первый td перед началом второго. У меня нет возможности записать это сейчас, сделаю это через несколько часов, если вам все еще нужно. – Woody