Подсчитайте общее количество предшествующих братьев и сестер и комментариев - PullRequest
0 голосов
/ 15 апреля 2019

У меня есть много файлов, похожих на следующий файл .xml, хотя они намного больше:

<?xml version="1.0" encoding="UTF-8"?>
<a version="3.0">
  <b bb="P1">
    <!--============== b:P1 c:1 ==============-->
    <c cc="1">
      <d dd="61">d1
      </d>
    </c>
    <!--============== b:P1 c:2 ==============-->
    <c cc="2">
      <d dd="17">d2
      </d>
    </c>
  </b>
</a>

Для каждого c есть только один предыдущий комментарий.

Я хочувыведите файл с такой же структурой, что и следующий файл a.csv:

1|1|a|0| |0| |0|
2|1|a|1|b|0| |0|
3|1|a|1|b|1|!|0|
3|1|a|1|b|2|c|0|
4|1|a|1|b|2|c|1|d
3|1|a|1|b|3|!|0|
3|1|a|1|b|4|c|0|
4|1|a|1|b|4|c|1|d

Он представляет собой иерархическое дерево для a.xml:

  1. Поле 1 - это полеиерархический уровень.Например, a имеет уровень 1, b имеет уровень 2 и т. Д.

  2. Поля 2, 4, 6 и 8 равны:

    • , если текущийуровень узла меньше уровня текущего поля, чем 0
    • , иначе общее количество предшествующих братьев и сестер и комментариев плюс один
  3. Поле 3, 5, 7 и9 равны:

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

В этом примере уровень 3 содержит комментарии.

Я не могу найти хороший способ сделатьдля каждого, который включает в себя как узлы, так и комментарии.Когда я использую <xsl:for-each select="*">, я только зацикливаюсь на узлах.

Из-за этого я получил следующий xslt, который проверяет, предшествует ли текущему узлу комментарий:

<?xml version="1.0"?>
<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()">
  <xsl:copy>
   <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/">
  <xsl:for-each select="*">
   <xsl:variable name="elm01" select="local-name()" />
   <xsl:text>1</xsl:text>
   <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
   <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
   <xsl:text>|0| |0| |0|</xsl:text>
   <xsl:text>&#10;</xsl:text>
   <xsl:for-each select="*">
    <xsl:variable name="elm02" select="local-name()" />
    <xsl:text>2</xsl:text>
    <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
    <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
    <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
    <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
    <xsl:text>|0| |0|</xsl:text>
    <xsl:text>&#10;</xsl:text>
    <xsl:for-each select="*">
     <xsl:variable name="elm03" select="local-name()" />
     <xsl:if test="preceding-sibling::comment()[1]">
      <xsl:text>3</xsl:text>
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
      <xsl:text>|!</xsl:text>
      <xsl:text>|0|</xsl:text>
      <xsl:text>&#10;</xsl:text>
     </xsl:if>
     <xsl:text>3</xsl:text>
     <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
     <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
     <!-- TODO: I want to count the total of preceding siblings and comments -->
     <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|comment())+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
     <xsl:text>|0|</xsl:text>
     <xsl:text>&#10;</xsl:text>
     <xsl:for-each select="*">
      <xsl:variable name="elm04" select="local-name()" />
      <xsl:text>4</xsl:text>
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm04" />
      <xsl:text>&#10;</xsl:text>
     </xsl:for-each>
    </xsl:for-each>
   </xsl:for-each>
  </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

Однако, когда я запускаю следующую команду:

xsltproc a.xslt a.xml > a.csv

Я получаю следующий файл a.csv:

1|1|a|0| |0| |0|
2|1|a|1|b|0| |0|
3|1|a|1|b|1|!|0|
3|1|a|1|b|1|c|0|
4|1|a|1|b|1|c|1|d
3|1|a|1|b|2|!|0|
3|1|a|1|b|2|c|0|
4|1|a|1|b|2|c|1|d

Обратите внимание, что поле 6 неверно:

  • равно 1 как для 1-го комментария, так и для 1-го узла c и его дочерних элементов

  • равно 2 для 2-го комментария и2-й узел c и его потомки

Есть ли у вас какие-либо решения, чтобы предложить?

РЕШЕНИЕ (Тим)

Теперь я могу получить правильныйвывод с использованием следующего файла xslt:

<?xml version="1.0"?>
<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()">
  <xsl:copy>
   <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/">
  <xsl:for-each select="*">
   <xsl:variable name="elm01" select="local-name()" />
   <xsl:text>1</xsl:text>
   <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
   <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
   <xsl:text>|0| |0| |0|</xsl:text>
   <xsl:text>&#10;</xsl:text>
   <xsl:for-each select="*">
    <xsl:variable name="elm02" select="local-name()" />
    <xsl:text>2</xsl:text>
    <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
    <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
    <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
    <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
    <xsl:text>|0| |0|</xsl:text>
    <xsl:text>&#10;</xsl:text>
    <xsl:for-each select="*">
     <xsl:variable name="elm03" select="local-name()" />
     <xsl:if test="preceding-sibling::comment()[1]">
      <xsl:text>3</xsl:text>
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment())"/>
      <xsl:text>|!</xsl:text>
      <xsl:text>|0|</xsl:text>
      <xsl:text>&#10;</xsl:text>
     </xsl:if>
     <xsl:text>3</xsl:text>
     <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
     <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
     <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment())+1"/>
     <xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
     <xsl:text>|0|</xsl:text>
     <xsl:text>&#10;</xsl:text>
     <xsl:for-each select="*">
      <xsl:variable name="elm04" select="local-name()" />
      <xsl:text>4</xsl:text>
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*|../preceding-sibling::comment())+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
      <xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
      <xsl:text>|</xsl:text><xsl:copy-of select="$elm04" />
      <xsl:text>&#10;</xsl:text>
     </xsl:for-each>
    </xsl:for-each>
   </xsl:for-each>
  </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

В качестве альтернативы вы также можете использовать xslt в ответе Тима, который избавляет от повторений.

1 Ответ

1 голос
/ 15 апреля 2019

Есть выражение, которое вам нужно, это ...

<xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment()) + 1" />

Или это тоже будет работать ...

<xsl:value-of select="count(preceding-sibling::node()[self::*|self::comment()]) + 1" />

Но вы также можете использовать xsl:number

<xsl:number count="*|comment()" />

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

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text" />

  <xsl:param name="maxLevel" select="4" />

  <xsl:template match="*|comment()">
    <xsl:param name="level" select="1" />
    <xsl:param name="prev" />
    <xsl:variable name="new">
        <xsl:value-of select="$prev" />
        <xsl:text>|</xsl:text>
        <xsl:number count="*|comment()" />
        <xsl:text>|</xsl:text>
        <xsl:choose>
            <xsl:when test="self::*">
                <xsl:value-of select="local-name()" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:text>!</xsl:text>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>

    <xsl:value-of select="$level" />
    <xsl:value-of select="$new" />
    <xsl:call-template name="pad">
        <xsl:with-param name="levels" select="$maxLevel - $level" />
    </xsl:call-template>

    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="*|comment()">
        <xsl:with-param name="level" select="$level + 1" />
        <xsl:with-param name="prev" select="$new" />
    </xsl:apply-templates>        
  </xsl:template>

  <xsl:template name="pad">
      <xsl:param name="levels" />
      <xsl:if test="$levels > 0">
          <xsl:text>|0| </xsl:text>
          <xsl:call-template name="pad">
              <xsl:with-param name="levels" select="$levels - 1" />
          </xsl:call-template>
      </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Смотрите его в действии на http://xsltfiddle.liberty -development.net / jyRYYiy

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