Вывод значений определенным образом с использованием XSLT / XPath 2.0 - PullRequest
2 голосов
/ 26 февраля 2012

У меня есть такой XML-код:

<?xml version="1.0" encoding="UTF-8"?>

<Section>
    <Chapter>
        <Cell colname="1">
            <Value>A</Value>
        </Cell>
        <Cell colname="2">
            <MyValue>AAA</MyValue>
            <MyValue>BBB</MyValue>
        </Cell>
        <Cell colname="3">
            <MyCar>Honda</MyCar>
        </Cell>
    </Chapter>
    <Chapter>
        <Cell colname="1">
            <Value>C</Value>
        </Cell>
        <Cell colname="2">
            <MyValue>CCC</MyValue>
        </Cell>
        <Cell colname="3">
            <MyCar>Toyota</MyCar>
        </Cell>
    </Chapter>
</Section>

Мне нравится вывод сообщения (позже конвертируем их в теги):

A AAA Honda ВВВ Honda С CCC Toyota

Это мой XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:output method="xml" version="1.0" encoding="iso-8859-1"   indent="yes"/>

    <xsl:template match="/">
        <xsl:apply-templates select="Section//Chapter"/>
    </xsl:template>

    <xsl:template match="Chapter">
        <xsl:for-each select="Cell[@colname='2']//MyValue">
            <xsl:message>
                <xsl:value-of select="Cell[@colname='1']/Value"/>
                <xsl:value-of select="."/>
                <xsl:value-of select="Cell[@colname='3']/MyCar"/>
            </xsl:message>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="text()" />

</xsl:stylesheet>

К сожалению, он не выводит то, что я хотел бы сделать: (.

Я понимаю, что for-each изменит контекст, поэтому оставшиеся значения ничего не сделают.

Каким было бы решение для этого?

ТИА

John

Ответы [ 2 ]

3 голосов
/ 26 февраля 2012

Это преобразование XSLT 2.0 (эквивалентное преобразование XSLT 1.0 может быть механически записано из этого).

Примечание : Это общее решение, которое работаетс любым числом детей и не зависит от жестко закодированных имен.

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="Chapter">
  <xsl:apply-templates select="Cell[1]"/>
 </xsl:template>

 <xsl:template match="Cell">
  <xsl:param name="pText" as="xs:string*"/>

  <xsl:apply-templates select="*[1]">
   <xsl:with-param name="pText" select="$pText"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="Cell/*">
  <xsl:param name="pText" as="xs:string*"/>

  <xsl:variable name="vText" as="xs:string*"
   select="$pText, string(.)"/>

  <xsl:sequence select=
   "$vText
      [not(current()/../following-sibling::Cell)]"/>
  <xsl:apply-templates select="../following-sibling::Cell[1]">
    <xsl:with-param name="pText" select="$vText"/>
  </xsl:apply-templates>
  <xsl:apply-templates select="following-sibling::*">
    <xsl:with-param name="pText" select="$pText"/>
  </xsl:apply-templates>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<Section>
    <Chapter>
        <Cell colname="1">
            <Value>A</Value>
        </Cell>
        <Cell colname="2">
            <MyValue>AAA</MyValue>
            <MyValue>BBB</MyValue>
        </Cell>
        <Cell colname="3">
            <MyCar>Honda</MyCar>
        </Cell>
    </Chapter>
    <Chapter>
        <Cell colname="1">
            <Value>C</Value>
        </Cell>
        <Cell colname="2">
            <MyValue>CCC</MyValue>
        </Cell>
        <Cell colname="3">
            <MyCar>Toyota</MyCar>
        </Cell>
    </Chapter>
</Section>

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

A AAA Honda A BBB Honda C CCC Toyota

Объяснение :

  1. По сути, это задача для получения всех комбинаций значенийкоторые принадлежат к N группам (дети Cell составляют группу), беря по одному элементу из каждой группы.

  2. Для любого элемента группы K мы добавляем этот элемент втекущая комбинация предметов групп 1, 2, ..., к-1.Затем мы передаем эту вновь сформированную комбинацию группе K + 1.Если мы являемся элементом последней группы (N), мы распечатываем (xsl:sequence) всю накопленную комбинацию.

  3. Когда элемент управления возвращается, все комбинации элементов оставшихсягруппы (K + 1, K + 2, ..., N) были добавлены к нашей текущей комбинации элементов групп групп 1, 2, ..., K. Все эти комбинации уже напечатаны.

  4. Мы передаем ту же комбинацию элементов группы, которая была передана нам - теперь мы передаем ее следующему элементу в текущей группе (следующий брат):

1 голос
/ 26 февраля 2012

Я немного изменил ваш подход, потому что я предполагаю, что вы действительно не хотите использовать <xsl:message> диагностическую операцию.Кроме того, я не генерирую XML на выходе и не использую for-each:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="text" version="1.0" encoding="iso-8859-1"   indent="yes"/>
    <xsl:template match="/Section">
        <xsl:apply-templates select="Chapter"/>
    </xsl:template>
    <xsl:template match="Chapter">
        <xsl:apply-templates select="Cell/MyValue" />
    </xsl:template>
    <xsl:template match="MyValue">
            <xsl:value-of select="../../Cell[@colname='1']/Value/text()"/>
            <xsl:text> </xsl:text>
            <xsl:value-of select="."/>
            <xsl:text> </xsl:text>
            <xsl:value-of select="../../Cell[@colname='3']/MyCar"/>
            <xsl:text> </xsl:text>
    </xsl:template>
</xsl:stylesheet>
...