将特定节点移动到节点A,并仅移动t节点的两次出现之间的节点

时间:2011-07-23 18:03:58

标签: xml xslt xpath

我想用XSLTXQuery修复XML文件(从文本文件生成),因此我可以用它做一些有用的事情。这是输入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 个答案:

答案 0 :(得分:1)

以下是使用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>

答案 1 :(得分:1)

你想要的是基于两个节点集相对于main上下文的交集来简单重新排列输入XML:

  • A 下一个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.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>