Оператор XPath, чтобы найти количество таких же предшествующих братьев и сестер - PullRequest
0 голосов
/ 21 января 2020

У меня есть следующая XML структура

<containers>
    <container type="1"></container>
    <container type="2"></container>
    <container type="2"></container>
    <container type="1"></container>
    <container type="2"></container>
    <container type="2"></container>
    <container type="2"></container> 
</containers>

Я хочу найти количество непосредственных предшествующих братьев и сестер того же типа. Как проверить в XSLT, например,

  1. для подсчета 1-го контейнера предшествующего брата того же типа = 0
  2. для подсчета 2-го контейнера предшествующего брата того же типа = 0
  3. для 3-го числа контейнеров предшествующего брата одного и того же типа = 1
  4. для 4-го контейнера для предшествующего брата того же типа = 0
  5. для 5-го числа контейнера предшествующего родного брата тот же тип = 0
  6. для 6-го числа контейнеров предшествующего брата того же типа = 1
  7. для 7-го контейнера для предшествующего брата того же типа = 2

мое состояние XSLT

    <xsl:choose>
  <xsl:when test="count(preceding-sibling::*[type = 2]) mod 2 = 0">
    <xsl:attribute name="class">classX</xsl:attribute>
  </xsl:when>
  <xsl:otherwise>   
  </xsl:otherwise>
</xsl:choose> 

Ответы [ 2 ]

1 голос
/ 21 января 2020

В XSLT-2.0 это легко:

<xsl:template match="containers">
  <xsl:copy>
    <xsl:for-each-group select="container" group-adjacent="@type">
      <xsl:for-each select="current-group()">
        <container>
            <xsl:copy-of select="@*" />
            <xsl:attribute name="class"><xsl:value-of select="position()-1" /></xsl:attribute>
        </container>
      </xsl:for-each>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

Его вывод:

<?xml version="1.0" encoding="UTF-8"?>
<containers>
   <container type="1" class="0"/>
   <container type="2" class="0"/>
   <container type="2" class="1"/>
   <container type="1" class="0"/>
   <container type="2" class="0"/>
   <container type="2" class="1"/>
   <container type="2" class="2"/>
</containers>

Решение XSLT-1.0 это (вдохновлено этим Ответ SO ):

<xsl:template match="containers">
  <xsl:copy>
    <xsl:for-each select="container">        
      <container>
        <xsl:copy-of select="@*" />
        <xsl:attribute name="class"><xsl:value-of select="count(preceding-sibling::container) - count(preceding-sibling::container[@type!=current()/@type][1]/preceding-sibling::container | preceding-sibling::container[@type!=current()/@type][1])" /></xsl:attribute>
      </container>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>

Это сложнее и медленнее - и гораздо менее элегантно. Но это делает свою работу. Выход такой же.

0 голосов
/ 21 января 2020

В XSLT 3 вы можете реализовать его с помощью аккумулятора:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy" streamable="yes" use-accumulators="type-count"/>

  <xsl:accumulator name="type-count" as="map(*)" initial-value="map{}" streamable="yes">
      <xsl:accumulator-rule match="containers" select="map {}"/>
      <xsl:accumulator-rule
        match="containers/container"
        select="map { 
                  'type' : string(@type), 
                  'count' :
                     if (not(@type = $value?type))
                     then 0
                     else $value?count + 1
                }"/>
  </xsl:accumulator>

  <xsl:template match="container">
      <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:attribute name="sibling-count" select="accumulator-before('type-count')?count"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/gWEaSuW

Или, например, проверить и создать атрибут:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy" streamable="yes" use-accumulators="type-count"/>

  <xsl:accumulator name="type-count" as="map(*)" initial-value="map{}" streamable="yes">
      <xsl:accumulator-rule match="containers" select="map {}"/>
      <xsl:accumulator-rule
        match="containers/container"
        select="map { 
                  'type' : string(@type), 
                  'count' :
                     if (not(@type = $value?type))
                     then 0
                     else $value?count + 1
                }"/>
  </xsl:accumulator>

  <xsl:template match="container">
      <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:if test="accumulator-before('type-count')?count mod 2 = 0">
              <xsl:attribute name="class">classX</xsl:attribute>
          </xsl:if>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/gWEaSuW/1

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...