Получить первый дочерний узел в XSLT с помощью local-name()

предположим, что у нас есть этот простой xml ...

 <books>   
    <book>
       <author/>
       <title/>
    </book>
    <book>
       <author/>
       <title/>
    </book>
 </books>

Я использую этот xpath для получения элементов первого экземпляра книги.

//books[1]/*

возвращает

<author/>
<title/>

и это отлично работает, но я должен заставить его работать с помощью local-name(). Я пробовал следующее, Но ничего из этого не работает...

//*[local-name()='books']/*

это возвращает повторяющиеся элементы автора и заголовка, не хорошо, мне нужны только от первого ребенка

//*[local-name()='books'][0]/*

это не вернуть ничего

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

author,title
john,The End is Near
sally,Looking for Answers

3 ответов


выражение пути, которое вы говорите, работает для вас

//books[1]/*

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

/books/*

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

трудно знать, что вам нужно, если вы всегда обращаетесь local-name к корневому узлу тогда вам не нужно знать его имя и доступ к нему только /*, так что вы хотели просто

/*/*[1]

однако для доступа к первому дочернему узлу узла в любом месте документа вы напишете

//*[local-name()='books']/*[1]

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


это FAQ -- XPath [] оператор имеет более высокий приоритет (приоритет), чем // псевдо-оператора.

так:

//someElemName[1]

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

чтобы изменить это, необходимо использовать скобки.

использовать:

(//*[local-name() = 'book'])[1]/*

также Примечание!--10-->: В XPath позиции основаны на 1, а не на 0.

проверка на основе XSLT:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:copy-of select=
  "(//*[local-name() = 'book'])[1]/*"/>
 </xsl:template>
</xsl:stylesheet>

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

<books>
    <book num="1">
        <author num="1"/>
        <title num="1"/>
    </book>
    <book num="2">
        <author num="2"/>
        <title num="2"/>
    </book>
</books>

нужные узлы выбираются и копируются на выход:

<author num="1"/>
<title num="1"/>

Я должен удовлетворить те же проблемы. Я решил так:

//*[local-name()='MYNODENAME' and position()=X]

хорошего дня.