Цикл XSLT — четыре узла одновременно

Во-первых, я знаю об этом вопросе: XSLT: цикл, выбирающий два элемента одновременно время

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

<input>
  <node>
    <id>1</id>
    <value>3</value>
  </node>
  <node>
    <id>1</id>
    <value>3</value>
  </node>
  <node>
    <id>1</id>
    <value>3</value>
  </node>
  <node>
    <id>1</id>
    <value>3</value>
  </node>
  <node>
    <id>2</id>
    <value>4</value>
  </node>
  <node>
    <id>2</id>
    <value>4</value>
  </node>
  <node>
    <id>2</id>
    <value>4</value>
  </node>
  <node>
    <id>2</id>
    <value>4</value>
  </node>
</input>

У меня есть следующий макет XML, который имеет следующую структуру: - узлы с одним и тем же идентификатором ВСЕГДА будут сгруппированы вместе - всегда будет четыре узла для одного идентификатора

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

Как лучше всего подойти к этому?


person Mike    schedule 29.08.2012    source источник


Ответы (3)


Этот XSLT:

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

<xsl:key name="keyByID" match="node" use="id"/>

<xsl:template match="/">
    <output>
        <xsl:apply-templates/>
    </output>
</xsl:template>

<xsl:template match="input">
    <xsl:for-each select="node[generate-id()=generate-id(key('keyByID',id)[1])]">
        <block>
            <id>
                <xsl:value-of select="id"/>
            </id>
            <value>
                <xsl:value-of select="value"/>
            </value>
        </block>
    </xsl:for-each>
</xsl:template>


</xsl:stylesheet>

применяется к вашему входному XML:

<?xml version="1.0" encoding="UTF-8"?>
<input>
<node>
    <id>1</id>
    <value>3</value>
</node>
<node>
    <id>1</id>
    <value>3</value>
</node>
<node>
    <id>1</id>
    <value>3</value>
</node>
<node>
    <id>1</id>
    <value>3</value>
</node>
<node>
    <id>2</id>
    <value>4</value>
</node>
<node>
    <id>2</id>
    <value>4</value>
</node>
<node>
    <id>2</id>
    <value>4</value>
</node>
<node>
    <id>2</id>
    <value>4</value>
</node>
</input>

дает этот сгруппированный выходной XML:

<?xml version="1.0" encoding="UTF-8"?>
<output>
<block>
    <id>1</id>
    <value>3</value>
</block>
<block>
    <id>2</id>
    <value>4</value>
</block>
</output>

Вывод группируется по <id>. Это то, что вы ищете? Я не уверена. Эта мюнхенская группировка просто упрощает вашу структуру.

С уважением, Питер

person Peter    schedule 29.08.2012

Поскольку элементы node гарантировано всегда находятся в группах по 4, желаемый результат может быть получен с помощью очень простого преобразования, которое не использует какой-либо метод группировки (например, Мюнхенское или родственное сравнение) и, вероятно, более эффективно:

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

 <xsl:template match="node()|@*" name="identity">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="node">
  <sample>
   <xsl:call-template name="identity"/>
  </sample>
 </xsl:template>
 <xsl:template match="node[not(position() mod 4 = 1)]"/>
</xsl:stylesheet>

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

<input>
  <node>
    <id>1</id>
    <value>3</value>
  </node>
  <node>
    <id>1</id>
    <value>3</value>
  </node>
  <node>
    <id>1</id>
    <value>3</value>
  </node>
  <node>
    <id>1</id>
    <value>3</value>
  </node>
  <node>
    <id>2</id>
    <value>4</value>
  </node>
  <node>
    <id>2</id>
    <value>4</value>
  </node>
  <node>
    <id>2</id>
    <value>4</value>
  </node>
  <node>
    <id>2</id>
    <value>4</value>
  </node>
</input>

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

<input>
   <sample>
      <node>
         <id>1</id>
         <value>3</value>
      </node>
   </sample>
   <sample>
      <node>
         <id>2</id>
         <value>4</value>
      </node>
   </sample>
</input>

Пояснение:

  1. правило идентификации копирует "как есть" каждые узел, для которого он выбран для выполнения.

  2. Другой шаблон переопределяет шаблон идентификации для каждого элемента node, который является 4k+1st node дочерним элементом своего родителя. Этот шаблон создает элемент-оболочку (sample), а затем вызывает шаблон удостоверения по имени, чтобы скопировать себя в выходные данные.

  3. Еще один шаблон переопределяет шаблон идентификации для каждого элемента node. Этот шаблон соответствует каждому элементу node, но он будет выбран для выполнения (предпочтительнее предыдущего шаблона) только для узлов, не соответствующих предыдущему шаблону, то есть для любого элемента node, который не является 4k+1st node дочерним элементом своего родителя. Это так, потому что этот шаблон менее конкретен, чем предыдущий шаблон.

  4. Шаблон, рассмотренный в пункте 3. выше, не имеет тела, и это эффективно «удаляет» соответствующий элемент node из вывода.

person Dimitre Novatchev    schedule 29.08.2012

Если вы можете гарантировать, что узлы с одним и тем же идентификатором всегда будут находиться рядом, то

<xsl:template match="node[not(preceding-sibling::node[1]/id = id])">

Будет соответствовать первому узлу для каждого идентификатора (технически любому узлу, чей идентификатор отличается от идентификатора узла перед ним, если он есть), и в этом шаблоне вы можете использовать following-sibling:: для поиска остальных, или просто определите ключ на верхнем уровне, а затем используйте его для извлечения всех узлов с одним и тем же идентификатором.

person Ian Roberts    schedule 29.08.2012