Вопрос

Учитывая следующую структуру:

<TITEL1>...</TITEL1>  
<p>..</p>
<TITEL2>...</TITEL2>  
<TITEL3>...</TITEL3>
<TITEL3>...</TITEL3>  
<P>...<P>  

Есть ли способ добраться до этого:

<TITEL1>
    <TITEL>...</TITEL>  
    <p>...</p>
    <TITEL2>
        <TITEL>...</TITEL>  
        <TITEL3>
            <TITEL>...</TITEL>
            <P>...</P>
        </TITEL3>
        <TITEL3>
            <TITEL>...</TITEL>
            <P>...</P>
        </TITEL3>
    </TITEL2>
</TITEL1>

Или, другими словами, есть ли способ, чтобы титы более высокого уровня включали титы более низкого уровня и весь контент, который следует за ними, создавая тем самым вложенную структуру. Содержание каждого тега Titel1,2 и 3 должно перейти в новый <TITEL>-элемент

Это было полезно?

Решение

С XSLT 2.0 (как реализовано Saxon 9 или Altovaxml Tools) вы можете использовать XSL: для начала группы в группе с группой и рекурсивной функцией:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/2010/mf"
  exclude-result-prefixes="xsd mf">

  <xsl:output indent="yes"/>

  <xsl:function name="mf:nest" as="element()*">
    <xsl:param name="elements" as="element()*"/>
    <xsl:param name="level" as="xsd:integer"/>
    <xsl:for-each-group select="$elements" group-starting-with="*[starts-with(local-name(), concat('TITEL', $level))]">
      <xsl:choose>
        <xsl:when test="self::*[starts-with(local-name(), concat('TITEL', $level))]">
          <xsl:element name="TITEL{$level}">
            <xsl:apply-templates select="."/>
            <xsl:sequence select="mf:nest(current-group() except ., $level + 1)"/>
          </xsl:element>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="current-group()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each-group>
  </xsl:function>

  <xsl:template match="ROOT">
    <xsl:sequence select="mf:nest(*, 1)"/>
  </xsl:template>

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

  <xsl:template match="*[starts-with(local-name(), 'TITEL')]">
    <TITEL>
      <xsl:apply-templates select="@* | node()"/>
    </TITEL>
  </xsl:template>

</xsl:stylesheet>

С этой таблицей стилей вход

<ROOT>
<TITEL1>Titel 1, 1</TITEL1>  
<p>..</p>
<TITEL2>Titel 2, 1</TITEL2>  
<TITEL3>Titel 3, 1</TITEL3>
<TITEL3>Titel 3, 2</TITEL3>  
<P>...</P>
</ROOT>

преобразуется в выход

<TITEL1>
   <TITEL>Titel 1, 1</TITEL>
   <p>..</p>
   <TITEL2>
      <TITEL>Titel 2, 1</TITEL>
      <TITEL3>
         <TITEL>Titel 3, 1</TITEL>
      </TITEL3>
      <TITEL3>
         <TITEL>Titel 3, 2</TITEL>
         <P>...</P>
      </TITEL3>
   </TITEL2>
</TITEL1>

Другие советы

Там нет особенно залаженного способа сделать то, что вы хотите. Это (вероятно) возможно, но это будет включать в себя довольно уродливые (и медленные) запросы XPath, используя following-sibling ось с фильтрами на preceding-sibling Ось соответствует текущему узлу.

Если это вообще возможно, я бы порекомендовал создать иерархию за пределами XSLT (в C#, Java и т. Д.)

Если вы решите идти по страшному пути, вы будете стремиться сделать что -то подобное (непроверенно):

<xsl:template match="TITEL1">
  <TITEL1>
    <xsl:apply-templates 
      select="following-sibling::(p|TITEL2)[(preceding-sibling::TITEL1)[1]=.]" />
  </TITEL1>
</xsl:template>

<xsl:template match="TITEL2">
  <TITEL1>
    <xsl:apply-templates 
      select="following-sibling::(p|TITEL3)[(preceding-sibling::TITEL2)[1]=.]" />
  </TITEL1>
</xsl:template>

...

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

Если вы не можете использовать XSLT 2.0, вот таблица стилей XSLT 1.0, которая должна получить тот же результат, что и таблица стилей XSLT 2.0, которую я разместил ранее:

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

  <xsl:output indent="yes"/>

  <xsl:template match="ROOT">
    <xsl:apply-templates select="*[1]" mode="nest">
      <xsl:with-param name="level" select="1"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="*[starts-with(local-name(), 'TITEL')]" mode="nest">
    <xsl:param name="level"/>
    <xsl:choose>
      <xsl:when test="$level = substring-after(local-name(), 'TITEL')">
        <xsl:element name="TITEL{$level}">
          <xsl:apply-templates select="."/>
          <xsl:apply-templates select="following-sibling::*[1][not(starts-with(local-name(), concat('TITEL', $level)))]" mode="nest">
            <xsl:with-param name="level" select="$level"/>
          </xsl:apply-templates>
        </xsl:element>
        <xsl:apply-templates select="following-sibling::*[starts-with(local-name(), concat('TITEL', $level))][1]" mode="nest">
          <xsl:with-param name="level" select="$level"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="." mode="nest">
          <xsl:with-param name="level" select="$level + 1"/>
        </xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="*[not(starts-with(local-name(), 'TITEL'))]" mode="nest">
    <xsl:param name="level"/>
    <xsl:apply-templates select="."/>
    <xsl:apply-templates select="following-sibling::*[1][not(starts-with(local-name(), concat('TITEL', $level)))]" mode="nest">
      <xsl:with-param name="level" select="$level"/>
    </xsl:apply-templates>
  </xsl:template>

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

  <xsl:template match="*[starts-with(local-name(), 'TITEL')]">
    <TITEL>
      <xsl:apply-templates select="@* | node()"/>
    </TITEL>
  </xsl:template>

</xsl:stylesheet>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top