Переместить определенные узлы к узлу A и только между двумя вхождениями t-узла - PullRequest
1 голос
/ 23 июля 2011

Я хочу исправить файл XML (сгенерированный из текстового файла) с помощью XSLT или XQuery , чтобы я мог сделать что-то полезное с ним. Это входной XML-файл:

<root>
    <main><id>100</id></main>
    <child>1</child>
    <child>2</child>
    <main><id>200</id></main>
    <child>1</child>
    <child>2</child>
    <child>3</child>
    <main><id>300</id></main>
    <child>1</child>
</root>

Это вывод, который мне нравится иметь

<root>
    <main>
        <id>100</id>
        <children>
            <child>1</child>
            <child>2</child>
        </children>
    </main>
    <main>
        <id>200</id>
        <children>
            <child>1</child>
            <child>2</child>
            <child>3</child>
        </children>
    </main>
    <main>
        <id>300</id>
        <children>
            <child>1</child>
        </children>
    </main>
</root>

Ответы [ 2 ]

1 голос
/ 25 июля 2011

Требуется простая перестановка входного XML на основе пересечения двух наборов узлов в отношении контекста main:

  • A allпредшествующие узлы следующего main или все последующие узлы последнего main узла

    "following-sibling::main[1]
       /preceding-sibling::node()
     | 
     following-sibling::*[count(current()/following::main[1])=0]"
    
  • B все последующие узлы текущего main узел

    "following-sibling::*"
    

В [XSLT 1.0] пересечение представляется как:

    "$B[count(.|$A)=count($A)]"

В [XSLT 2.0] используется оператор intersect:

    "$A intersect $B"

Следующее преобразование является примером решения XSLT 1.0

Примечание Это преобразование использует хорошо известное преобразование Identity путем включения его из внешнего файла identity.xsl

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:include href="identity.xsl"/>
    <xsl:template match="child"/>

    <xsl:template match="main">
        <xsl:variable name="A" 
            select="
            following-sibling::main[1]
                /preceding-sibling::node()
            |
            following-sibling::*
            [count(current()/following::main[1])=0]"/>
        <xsl:variable name="B" select="following-sibling::*"/>
        <xsl:copy>
            <xsl:apply-templates select="@*|id"/>
            <children>
                <xsl:apply-templates select="$B[count(.|$A) = count($A)]" 
                   mode="copy"/>
            </children>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="child" mode="copy">
        <xsl:copy-of select="."/>
    </xsl:template>

</xsl:stylesheet>
1 голос
/ 24 июля 2011

Вот решение с XSLT 1.0. Я прокомментировал как можно больше. Я использовал решение для прогулки по дереву. Прогулка с деревьями - не самая простая вещь (для начинающих) в XSLT. Для общего объяснения этого подхода смотрите здесь: Dave Pawson XSLT FAQ

Внутри узла <root> XSLT шаг за шагом проходит / проходит по прямым потомкам <root> и генерирует новый блок <main>...</main> (на каждом <main> узле) или <children>...</children> (<child>) который имеет прямой <main> родитель) на некоторых узлах.

XSLT:

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

<xsl:output method="xml" encoding="UTF-8" indent="yes"/>

<xsl:strip-space elements="*"/>

<xsl:template match="root">
  <xsl:copy>
    <!-- copy any attributes in root -->
    <xsl:apply-templates select="@*"/>
    <!-- walk through and copy nodes which are preceding main (if there are any?) -->
    <xsl:apply-templates select="*[1][not(self::main)]" mode="walker"/>
    <!-- start walking mode with first main -->
    <xsl:apply-templates select="*[self::main][1]" mode="walker"/>
  </xsl:copy>
</xsl:template>

<!-- do not copy main, it will be copied later in walker mode -->
<xsl:template match="root/main"/>

<xsl:template match="main" mode="walker">
    <main>
      <!-- copy any attributes of main -->
      <xsl:apply-templates select="@*"/>
      <!-- copy all children of main -->
      <xsl:apply-templates/>
      <!-- begin walking and copying the next following-sibling node IF it is not main -->
      <xsl:apply-templates select="following-sibling::*[1][not(self::main)]" mode="walker"/>
    </main>
    <!-- search the next main start and generate a new main block -->
    <xsl:apply-templates select="following-sibling::*[self::main][1]" mode="walker"/>
</xsl:template>

<!-- every child which has main as it first preceding-sibling generates a new children block -->
<xsl:template match="child[preceding-sibling::*[1][self::main]]" mode="walker">
    <children>
        <xsl:apply-templates select="."/>
        <!-- continue walking (through children) if the following-sibling node is NOT main -->
        <xsl:apply-templates select="following-sibling::*[1][not(self::main)]" mode="walker"/>
    </children>
</xsl:template>

<!-- copy all other nodes in walking mode -->
<xsl:template match="*" mode="walker">
    <!-- copy this node and children -->
    <xsl:apply-templates select="."/>
    <!-- walk to the next following-sibling IF it is not main -->
    <xsl:apply-templates select="following-sibling::*[1][not(self::main)]" mode="walker"/>
</xsl:template>

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

</xsl:stylesheet>

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

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <main>
    <id>100</id>
    <children>
      <child>1</child>
      <child>2</child>
    </children>
  </main>
  <main>
    <id>200</id>
    <children>
      <child>1</child>
      <child>2</child>
      <child>3</child>
    </children>
  </main>
  <main>
    <id>300</id>
    <children>
      <child>1</child>
    </children>
  </main>
</root>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...