我想用XSLT或XQuery修复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>
答案 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>