Не удается получить правильный вывод XSLT

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

<?xml version="1.0" encoding="UTF-8"?>
<Section>
    <Chapter>
        <nametable>
            <namerow>
                <namecell stuff="1">
                    <entity>A</entity>
                </namecell>
                <namecell stuff="2">
                    <entity>B</entity>
                </namecell>
            </namerow>
        </nametable>
    </Chapter>
</Section>

Мой XSLT выглядит так:

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

<xsl:output method="text"/>

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

    <xsl:template match="nametable">
        <xsl:for-each select="./namerow">
            <xsl:value-of select="./namecell/@stuff"/>
            <xsl:value-of select="./namecell" />
        </xsl:for-each>
    </xsl:template>

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

</xsl:stylesheet>

Странно, я получаю результат в таком порядке 1 2 A B, я думал, что получу 1 A 2 B.

Не уверен, почему это?.

ТИА,

Джон


person JohnX    schedule 24.02.2012    source источник


Ответы (3)


Ваша проблема здесь:

        <xsl:for-each select="./namerow">
            <xsl:value-of select="./namecell/@stuff"/>
            <xsl:value-of select="./namecell" />
        </xsl:for-each>

В отличие от XSLT 1.0, в XSLT 2.0 инструкция xsl:value-of выводит все элементы последовательности, указанные в ее атрибуте select.

Это означает, что

<xsl:value-of select="./namecell/@stuff"/>

выводит все атрибуты stuff (1 и 2)

затем следующая инструкция:

           <xsl:value-of select="./namecell" />

выводит строковое значение двух дочерних элементов namecell -- соответственно "A" и "B".

Решение:

Заменить:

        <xsl:for-each select="./namerow">
            <xsl:value-of select="./namecell/@stuff"/>
            <xsl:value-of select="./namecell" />
        </xsl:for-each>

с:

        <xsl:for-each select="./namerow">
            <xsl:value-of select="./namecell/(@stuff|entity)"/>
        </xsl:for-each>

Полный код с этой модификацией:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="2.0">

    <xsl:output method="text"/>

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

        <xsl:template match="nametable">
            <xsl:for-each select="./namerow">
                <xsl:value-of select="./namecell/(@stuff|entity)"/>
            </xsl:for-each>
        </xsl:template>

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

и когда это преобразование применяется к предоставленному XML-документу:

<Section>
    <Chapter>
        <nametable>
            <namerow>
                <namecell stuff="1">
                    <entity>A</entity>
                </namecell>
                <namecell stuff="2">
                    <entity>B</entity>
                </namecell>
            </namerow>
        </nametable>
    </Chapter>
</Section>

получен желаемый результат:

1 A 2 B

Второе решение (вероятно, то, что вы намеревались сделать в первую очередь):

Заменить:

    <xsl:template match="nametable">
        <xsl:for-each select="./namerow">
            <xsl:value-of select="./namecell/@stuff"/>
            <xsl:value-of select="./namecell" />
        </xsl:for-each>
    </xsl:template>

с:

    <xsl:template match="nametable">
        <xsl:for-each select="namerow/namecell">
            <xsl:value-of select="@stuff"/>
            <xsl:value-of select="entity"/>
        </xsl:for-each>
    </xsl:template>

Теперь вы снова получаете желаемый результат:

1A2B
person Dimitre Novatchev    schedule 24.02.2012
comment
Дмитрий: Что на самом деле делает этот ‹xsl:value-of select=./namecell/(@stuff|entity)/›? - person JohnX; 24.02.2012
comment
@JohnX: <xsl:value-of select="./namecell/(@stuff|entity)"/> выводит в виде текстовых узлов строковые значения каждого атрибута stuff и entity дочернего элемента каждого namecell дочернего элемента текущего узла. Здесь я использую оператор Xpath union |. Такое объединение в шаге местоположения возможно только в XPath 2.0 и синтаксически недопустимо в XPath 1.0. - person Dimitre Novatchev; 24.02.2012
comment
Dimitre: еще вопрос stackoverflow.com/questions/9448446/. Спасибо!. - person JohnX; 26.02.2012

Я использую msxml, получаю 1 A в качестве операции :|

Что ж, вот решение для вас, оно работает как драгоценный камень :)

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

  <xsl:output method="text"/>

  <xsl:template match="/Section/Chapter/nametable/namerow/namecell">
    <xsl:value-of select="@stuff"/>
    <xsl:text> </xsl:text><!--inserting a space-->
    <xsl:value-of select="entity"/>
    <xsl:text> </xsl:text><!--inserting a space-->
  </xsl:template>
  <xsl:template match="text()"/>

</xsl:stylesheet>
person InfantPro'Aravind'    schedule 24.02.2012
comment
Это то, что я сделал, чтобы получить правильный ответ, я использую Saxon EE, но я не понимаю, почему мой оригинальный пост не работает должным образом? - person JohnX; 24.02.2012
comment
Это может быть потому, что вы используете /namecell/@stuff вместо /@stuff .. Я не особо ориентируюсь на xslt 2.0, поэтому не уверен .. - person InfantPro'Aravind'; 24.02.2012
comment
msxml не позволяет :/ N фоновое кодирование моего инструмента основано на .net :( - person InfantPro'Aravind'; 24.02.2012

Причина поведения, которое вы видите, заключается в том, что вы перебираете namerow. Вы получите 1A2B, если вместо этого будете перебирать namecell как: <xsl:for-each select="namerow/namecell"><xsl:value-of select="@stuff"/><xsl:value-of select="." /></xsl:for-each>. Вы должны помнить, что каждый XPath, такой как @stuff, дает вам не обязательно одно значение (узел), а скорее набор значений (набор узлов). Этот набор обычно содержит только одно значение, но не дайте себя обмануть.

Другой синтаксис для того же самого: <xsl:for-each select="namerow"><xsl:for-each select="namecell"><xsl:value-of select="@stuff"/><xsl:value-of select="." /></xsl:for-each></xsl:for-each>

P.S. Как и в пути к файловой системе, вам не нужно писать «./» перед XPath.

person minopret    schedule 24.02.2012