#xslt #xpath
#xslt #xpath
Вопрос:
У меня есть XML, который выглядит следующим образом:
<A></A>
<A></A>
<A>
<a/>
<a/>
</A>
Как вы можете видеть, оно имеет два уровня <A>
и <a>
.
Я написал XSL transform, который генерирует индексный номер для каждого <A>
элемента, и это работает:
<xsl:template match "A">
<xsl:element name="Person">
<xsl:attribute name="id">
<xsl:number count="A"/>
</xsl:attribute>
</xsl:element>
</xsl>
Вывод:
<Person id="1"/>
<Person id="2"/>
<Person id="3"/>
Но как написать xsl: число, чтобы сгенерировать такое же число на <a>
уровне (at ???
)?
<xsl:template match "A">
<xsl:element name="Person">
<xsl:attribute name="id">
<xsl:number count="A"/>
<xsl:apply-templates select="a"/>
</xsl:attribute>
</xsl:element>
</xsl>
<xsl:template match "a">
<xsl:element name="Item">
<xsl:attribute name="id">
<xsl:number count="???"/>
</xsl:attribute>
</xsl:element>
</xsl>
Ожидаемый результат (я хочу то же самое id
для <Person>
и <Item>
):
<Person id="1"/>
<Person id="2"/>
<Person id="3">
<Item id="3"/>
<Item id="3"/>
</Person>
Я знаю, что это должно быть какое-то простое выражение XPATH, но я действительно застрял на этом.
Комментарии:
1. 😮 не могли бы вы, пожалуйста, предоставить желаемый результат?
2. @rekaszeru: ты прав. Я отредактировал свой вопрос.
3. почему вы хотите,
Item id
чтобы они не были уникальными? Если они должны быть ссылками на родительский узелPerson
, почему бы не сказать<Item personRef="3"/>
или что-то в этом роде? Я предполагаю, что это просто вопрос именования атрибута, но это сбивает с толку. Например. @Alejandro сгенерировал другой вывод, потому что он думал, что вы хотите, чтобыItem
элементы имели свои собственные идентификаторы.4. @LarsH: ну, правильно, `id’ должен был быть одинаковым для всех элементов, относящихся к одному и тому же человеку. Извините, если мой вопрос ввел в заблуждение. На самом деле, я искал решение, которое было в ответе Мартина.
Ответ №1:
Вы могли бы просто передать вычисленное число вниз, если вы действительно хотите того же:
<xsl:template match="A">
<xsl:variable name="id">
<xsl:number count="A"/>
</xsl:variable>
<Person id="{$id}">
<xsl:apply-templates select="a">
<xsl:with-param name="pid" select="$id"/>
</xsl:apply-templates>
</Person>
</xsl:template>
<xsl:template match="a">
<xsl:param name="pid"/>
<Item id="{$pid}"/>
</xsl:template>
Комментарии:
1. 1 Похоже, что на данный момент это единственное работающее решение. Спасибо
2. 1. Мне было интересно, можно ли получить выходные данные
<xsl:number>
в переменную. Почему бы и нет.3. Я пока не отмечаю это как окончательное решение. Возможно, это можно сделать без передачи переменной?
4. @Lukas, да, я опубликую отдельный ответ.
5. @LarsH: Я нашел ответ самостоятельно. Это опубликовано отдельно и очень просто.
Ответ №2:
В ответ на ваш запрос сделать это без передачи переменной, смотрите ниже. Недостаток заключается в том, что <xsl:number>
его нельзя использовать непосредственно в шаблоне значения атрибута (поскольку @Martin использовал $id
переменную), поэтому генерация id
атрибута становится подробной.
<xsl:template match="A">
<Person>
<xsl:attribute name="id">
<xsl:number count="A" />
</xsl:attribute>
<xsl:apply-templates select="a" />
</Person>
</xsl:template>
<xsl:template match="a">
<Item>
<xsl:attribute name="id"> <!-- I would name it personRef or something -->
<xsl:number count="A" />
</xsl:attribute>
</Item>
</xsl:template>
(Не проверено.)
Ключевым моментом здесь является использование select=".."
on xsl:number
в "a"
шаблоне. Редактировать: Оказывается, что select=".."
на самом деле это не обязательно. Поскольку контекстный узел a
не соответствует шаблону подсчета A
, он начинается с ближайшего предка, который ему соответствует. Какое множество полезных значений по умолчанию есть у этой инструкции!
Комментарии:
1. Это работает, хотя: xalan в Java выводит: Предупреждения компилятора: строка 92: Недопустимый атрибут ‘select’.
2. @Lukas: хорошо. Смотрите мою новую правку: выбор не нужен (протестировано в XSLT 2.0)
Ответ №3:
Хорошо, у меня есть ответ. Однако я отметил ответ Ларса как решение.
<xsl:template match "A">
<xsl:element name="Person">
<xsl:attribute name="id">
<xsl:number count="A"/>
<xsl:apply-templates select="a"/>
</xsl:attribute>
</xsl:element>
</xsl>
<xsl:template match "a">
<xsl:element name="Item">
<xsl:attribute name="id">
<xsl:number count="A" level="any"/>
</xsl:attribute>
</xsl:element>
</xsl>
Этого было достаточно, чтобы добавить level="any"
атрибут.
Комментарии:
1. @LarsH — Это так? Это даже не правильно сформированный XML.
2. Я не думаю, что
level="any"
это необходимо.count="A"
Должно быть достаточно. В XSLT 2.0 вы можете сделать код немного более компактным, поместивxsl:number
инструкцию внутри функции и вызвав ее из AVT:<Item id="my:number(.)">
3. @lwburk: вы правы, я всего лишь отреагировал на использование
<xsl:number count="A" level="any" />
и опустил переданный параметр. Некоторая некорректность кажется просто сокращением, но есть и более серьезные проблемы, такие как наличие результирующего элемента внутри значения атрибута. Смотрите мой ответ, чтобы найти решение, которое действительно работает.4. @Michael,
my:number(.)
вid
AVT должно быть в{
фигурных скобках}
, верно?5. @Michael Kay: 1 Это отличный комментарий. Это была боль из-за отсутствия какой-то
numbering()
функции.