xsl: число в дочернем узле

#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() функции.