XPath 1.0 выбирает отдельный атрибут братьев и сестер

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

Это пара узлов, которые у меня есть в файле xml (который создается из базы данных)

<PANELS>        
<PANEL ATTR1="7"  ATTR2="37" ATTR3="31"/>
<PANEL ATTR1="8"  ATTR2="37" ATTR3="31"/>
<PANEL ATTR1="8A" ATTR2="37" ATTR3="31"/>
</PANELS>
<ZONES>
<ZONE ATTR1="7"  ATTR2="37" ATTR3="31" />
<ZONE ATTR1="8"  ATTR2="37" ATTR3="31" />
<ZONE ATTR1="8A" ATTR2="37" ATTR3="31" />
</ZONES>

Я хочу иметь возможность выбрать отдельный ATTR3 из каждого из них.

В настоящее время это работает для первого //PANELS/PANEL[not(@ATTR3 = (preceding::*/@ATTR3))] и возвращает ожидаемый результат для «31».

Но когда я пытаюсь сделать то же самое для второго, он ничего не возвращает (я хочу, чтобы он снова возвращал «31») //ZONES/ZONE[not(@ATTR3 = (preceding::*/@ATTR3))]

Я понимаю, что второй не работает, потому что значение ATTR3 одинаково для всех из них, но как мне получить отдельное значение атрибута для каждого узла?

(Это используется как предикат для каждого, который я использую для отображения каждого отдельного значения)

Он используется следующим образом: один из этих for-each для ЗОН и один для ПАНЕЛЕЙ.

<xsl:for-each select="//PANELS/PANEL[not(@ATTR3 = (preceding::*/@ATTR3))]">
<xsl:sort select="@ATTR3"/>
<xsl:value-of select="@ATTR3" />
<xsl:if test="position()!=last()">, </xsl:if>
</xsl:for-each>

Я хотел бы, чтобы он вернулся

PANELS: 31

ZONES: 31

Я пытался использовать preceding-sibling вместо preceding, но потом получаю

PANELS: 31, 31

ZONES: 31

Каждый из них находится в таком шаблоне:

    <xsl:template match="//HEADER/ZONES" >              
    <fo:block font-size="10pt">
        <fo:table  table-layout="fixed" > 
            <fo:table-column column-width="proportional-column-width(1)"/>
            <fo:table-column column-width="proportional-column-width(7)"/>
            <fo:table-body>
                <fo:table-row>
                    <fo:table-cell  border-bottom="none">
                        <fo:block font-weight="bold">
                            <xsl:text>Zones:</xsl:text>
                        </fo:block>
                    </fo:table-cell >                       
                    <fo:table-cell>
                        <fo:block>
                            <xsl:for-each select="//HEADER/ZONES/ZONE[not(@ATTR3 = (preceding-sibling::*/@ATTR3))]">
                                <xsl:sort select="@ATTR3"/>
                                <xsl:value-of select="@ATTR3" />
                                <xsl:if test="position()!=last()">, </xsl:if>
                            </xsl:for-each>
                        </fo:block>
                    </fo:table-cell>
                </fo:table-row>             
            </fo:table-body>
        </fo:table>
    </fo:block>
    </xsl:template>

person froglander    schedule 06.07.2012    source источник
comment
Вы имеете в виду preceding-sibling вместо preceding?   -  person O. R. Mapper    schedule 06.07.2012
comment
Я пробовал с предыдущим братом, но в этом примере это дважды возвращает 31.   -  person froglander    schedule 06.07.2012
comment
Я думал, это то, что ты хотел? Один раз 31 из первого элемента <PANEL> и еще раз 31 из первого элемента <ZONE>. Вот что я получаю от //PANELS/PANEL[not(@ATTR3 = (preceding-sibling::*/@ATTR3))] | //ZONES/ZONE[not(@ATTR3 = (preceding-sibling::*/@ATTR3))].   -  person O. R. Mapper    schedule 06.07.2012
comment
Я пытаюсь получить их отдельно. Так что мне нужно 31 раз для ПАНЕЛИ и 31 раз для ЗОНЫ. Когда я пытаюсь использовать предыдущий брат в своей таблице стилей XSLT (сначала отображаются ПАНЕЛИ, а затем ЗОНЫ), я получаю ПАНЕЛИ: 31, 31 и ЗОНЫ: 31.   -  person froglander    schedule 06.07.2012
comment
Я получаю PANELS: 31 ZONES: 31, если вставлю приведенный выше фрагмент XSLT и эквивалентный фрагмент для <Zones> в шаблон, соответствующий /, используя preceding-siblings в обоих циклах <xsl:for-each>.   -  person O. R. Mapper    schedule 06.07.2012
comment
Будет ли тогда иметь значение, что это внутри такого шаблона? ‹xsl:template match=//HEADER/ZONES › и эквивалентный для ПАНЕЛЕЙ? Я попробовал это, как вы предложили, и все еще получаю ПАНЕЛИ: 31, 31   -  person froglander    schedule 06.07.2012
comment
У меня тоже работает. Выход: ZONES: 31 ZONES: 31   -  person O. R. Mapper    schedule 06.07.2012
comment
Я пытался заставить это работать все утро. Я удалил лишнюю часть //HEADER/ZONES/ и снова попытался выполнить предыдущий одноуровневый элемент, и на этот раз это сработало. Спасибо за помощь! Можно ли как-нибудь отметить ваш комментарий как ответ на мой вопрос? Мне жаль, что вы нашли мой вопрос недостаточно изученным - я искал все и пытался что-то безуспешно, поэтому, наконец, решил опубликовать свой вопрос.   -  person froglander    schedule 06.07.2012
comment
froglander: Этот код принятого ответа не только излишне многословен и сложен, и содержит фрагменты, которые никогда не выполняются (например, запятая, которая никогда не выводится), но и не дает нужного вам результата (строка Panels не не присутствует в сгенерированном выводе).   -  person Dimitre Novatchev    schedule 06.07.2012


Ответы (2)


Следующий XSLT использует preceding-siblings вместо preceding и, таким образом, дает правильное количество повторений 31:

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

    <xsl:output method="text"/>

    <xsl:template match="//HEADER/ZONES">
        ZONES:
        <xsl:for-each select="//ZONES/ZONE[not(@ATTR3 = (preceding-sibling::*/@ATTR3))]">
            <xsl:sort select="@ATTR3"/>
            <xsl:value-of select="@ATTR3" />
            <xsl:if test="position()!=last()">, </xsl:if>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="//HEADER/PANELS">
        PANELS:
        <xsl:for-each select="//PANELS/PANEL[not(@ATTR3 = (preceding-sibling::*/@ATTR3))]">
            <xsl:sort select="@ATTR3"/>
            <xsl:value-of select="@ATTR3" />
            <xsl:if test="position()!=last()">, </xsl:if>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

Вывод для этого документа:

<HEADER>
    <PANELS>
        <PANEL ATTR1="7"  ATTR2="37" ATTR3="31"/>
        <PANEL ATTR1="8"  ATTR2="37" ATTR3="31"/>
        <PANEL ATTR1="8A" ATTR2="37" ATTR3="31"/>
    </PANELS>
    <ZONES>
        <ZONE ATTR1="7"  ATTR2="37" ATTR3="31" />
        <ZONE ATTR1="8"  ATTR2="37" ATTR3="31" />
        <ZONE ATTR1="8A" ATTR2="37" ATTR3="31" />
    </ZONES>
</HEADER>

как следует:

    ZONES:
    31
    PANELS:
    31
person O. R. Mapper    schedule 06.07.2012
comment
Этот код не только излишне многословен и сложен, и содержит фрагменты, которые никогда не выполняются (например, запятая, которая никогда не выводится), но он также не дает результата, требуемого OP. - person Dimitre Novatchev; 06.07.2012

Это короткое (13 строк) и простое преобразование — всего один шаблон, без жестко заданных строк:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text" />
 <xsl:strip-space elements="*"/>

 <xsl:template match="t/*[*/@ATTR3]">
     <xsl:value-of select="concat(name(), ':')"/>
     <xsl:for-each select="*/@ATTR3[not(. = ../preceding-sibling::*/@ATTR3)]">
       <xsl:value-of select="concat(' ', .)"/>
     </xsl:for-each>
     <xsl:text> </xsl:text>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному XML-документу (завернутому в один верхний элемент, чтобы он стал правильно сформированным) документу:

<t>
    <PANELS>
        <PANEL ATTR1="7"  ATTR2="37" ATTR3="31"/>
        <PANEL ATTR1="8"  ATTR2="37" ATTR3="31"/>
        <PANEL ATTR1="8A" ATTR2="37" ATTR3="31"/>
    </PANELS>
    <ZONES>
        <ZONE ATTR1="7"  ATTR2="37" ATTR3="31" />
        <ZONE ATTR1="8"  ATTR2="37" ATTR3="31" />
        <ZONE ATTR1="8A" ATTR2="37" ATTR3="31" />
    </ZONES>
</t>

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

PANELS: 31 ZONES: 31 
person Dimitre Novatchev    schedule 06.07.2012
comment
Глядя на XSLT OP, я считаю, что идея состоит в том, чтобы генерировать значения, разделенные запятыми, в выводе для каждого из отдельных значений. В примере документа просто не оказалось нескольких различных значений. - person Mads Hansen; 24.12.2012
comment
@MadsHansen, это решение дает именно то, что ОП показал в своем вопросе как желаемый ответ. Я не хочу гадать (с большой вероятностью ошибки), что он хотел, но не показал в своем вопросе. Что касается различных различных значений, это решение дает правильные результаты, если имеется более одного различных значений атрибута. В качестве разделителя используется пробел, а не запятая. - person Dimitre Novatchev; 24.12.2012