使用XSLT将元素移动到不同的位置

时间:2013-05-23 23:07:54

标签: xml xslt

我已经成功创建了一个XSLT模板来复制一些元素,将一些元素的名称从一个XML文件更改为另一个XML文件。

但是,我无法弄清楚如何获取元素并将它们移动到XML结构的不同部分。

我想转换这个XML:

<Hosts>
    <Clusters>
        <Cluster>
            <Nodes>
                <WindowsHost/>
            </Nodes>
        </Cluster>
    </Clusters>
</Hosts>

为:

<Hosts>
    <WindowsHosts>
        <WindowsHost/>
    </WindowsHosts>
</Hosts>

我当前正在使用的XSLT包含:

<xsl:template match="/">
    <xsl:apply-templates select="*"/>
</xsl:template>

<xsl:template match="/*">
    <xsl:copy>
        <xsl:apply-templates select="*"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*">
    <xsl:apply-templates select="*"/>
</xsl:template>

...然后重复模板,如:

<xsl:template match="/Hosts/Clusters/Cluster/Nodes/WindowsHost">
    <xsl:copy>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

<xsl:template match="/Hosts/Clusters/Cluster/Nodes/WindowsHost/SomeElement">
    <xsl:copy>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

等等。要复制的每个元素都有自己的模板,因为不会复制所有元素并更改某些元素名称。但我只是成功地更改了元素名称,而不是完整的XPath。

任何帮助都将不胜感激。


非常感谢您的回复。但我认为我的例子太简单了,试图说清楚。

我现有的XSLT为每个需要从一个XML复制到另一个XML的元素都有一个模板,所以有很多模板(差不多1000个),XSLT文件的开头确保默认情况下没有任何元素发生。长话短说,我在你的答案中尝试了这些技巧并没有成功,可能是因为情况并不像我给你看的那样。

要复制的元素有三种情况:

  1. 按原样复制
  2. 复制但更改元素名称
  3. 复制到架构中的其他位置
  4. 现有的XSLT文件适用于#1和#2。这是#3我无法上班。这里有更多的XSLT文件

    <xsl:template match="/">
        <xsl:apply-templates select="*"/>
    </xsl:template>
    
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates select="*"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="*">
        <xsl:apply-templates select="*"/>
    </xsl:template>
    
    <xsl:template match="/SAN/ClientProfile">
    <!-- copy element as is (working) -->
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="/SAN/ClientProfile/Name">
    <!-- copy element but change its name (working) -->
        <CompanyName>
            <xsl:apply-templates/>
        </CompanyName>
    </xsl:template>
    
    <xsl:template match="/SAN/EQLHosts/WindowsClusters/Cluster/ClusterNodes/WindowsHost">
    <!-- copy to different part of schema (not working) -->
        <WindowsHost>
            <xsl:apply-templates/>
        </WindowsHost>
    </xsl:template>
    

    所以,我想将/SAN/EQLHosts/WindowsClusters/Cluster/ClusterNodes/WindowsHost更改为/SAN/EQLHosts/WindowsHosts/WindowsHost。此元素的所有子元素的处理方式与已复制元素的处理方式相同。

    我希望我的澄清清楚。如果这些信息改变了你的答案,或者我是否只是密集,请告诉我。

2 个答案:

答案 0 :(得分:1)

看起来你真的不动了什么;只是忽略一些元素并添加一个新的WindowsHosts元素。尝试使用一个indentity transform并更改xsl:apply-templates/*模板中选择的内容。

如果您仍需要重命名其他元素,只需添加一个与该元素特定匹配的新模板即可。身份转换将复制现有元素而不修改它们,因此您不必为每个元素都有一个模板。

示例:

XML输入

<Hosts>
    <Clusters>
        <Cluster>
            <Nodes>
                <WindowsHost/>
            </Nodes>
        </Cluster>
    </Clusters>
</Hosts>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output 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:copy>
            <WindowsHosts>
                <xsl:apply-templates select="@*|Clusters/Cluster/Nodes/WindowsHost"/>
            </WindowsHosts>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

输出

<Hosts>
   <WindowsHosts>
      <WindowsHost/>
   </WindowsHosts>
</Hosts>

修改

这是一个包含重命名的示例。它使用上面的输入产生相同的输出......

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="/*">
        <xsl:copy>
            <!--Start your select at the first element you need to output and/or modify.-->
            <xsl:apply-templates select="@*|Clusters/Cluster"/>
        </xsl:copy>
    </xsl:template>

    <!--Example rename-->
    <xsl:template match="Cluster">
        <WindowsHosts>
            <xsl:apply-templates select="@*|Nodes/WindowsHost"/>
        </WindowsHosts>
    </xsl:template>

</xsl:stylesheet>

编辑#2

  

所以,我想改变   /SAN/EQLHosts/WindowsClusters/Cluster/ClusterNodes/WindowsHost来   /SAN/EQLHosts/WindowsHosts/WindowsHost。这个元素的所有孩子   将以与已经存在的元素相同的方式处理   被复制。

在这种情况下,您要从WindowsHost ...

中选择EQLHosts
<xsl:template match="EQLHosts">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <WindowsHosts>
            <xsl:apply-templates select="WindowsClusters/Cluster/ClusterNodes/WindowsHost"/>
        </WindowsHosts>
    </xsl:copy>
</xsl:template>

如果您需要输出EQLHosts中的其他子项,则可以将其添加到第一个xsl:apply-templates。类似的东西:

<xsl:apply-templates select="@*|*[not(name()='WindowsClusters')]"/>

答案 1 :(得分:0)

由于将Daniel的答案与现有的XSLT文件结合使用,我创建了一个单独的XSLT文件,只是将这些元素移动到XML结构中的新位置。

此文件看起来像......

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

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

<xsl:template match="EQLHosts">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <WindowsHosts>
            <xsl:apply-templates select="WindowsClusters/Cluster/ClusterNodes/WindowsHost"/>
            <xsl:apply-templates select="WindowsClusters/Cluster/VirtualMachines/WindowsHost"/>
            <xsl:apply-templates select="WindowsHosts/WindowsHost"/>
        </WindowsHosts>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

......完美无缺。这个XSLT文件包含所有元素,同时移动一些元素。

我现有的XSLT文件排除所有元素,只复制指定的元素。因此,我认为拥有两个XSLT文件并不是一个坏主意,一个是一个接一个地运行。

除非有办法将它们合并,否则我会将它们保留为两个。