2

我是 xslt 的新手并面临一个问题。我必须使用 xslt 根据预定义的顺序对 xml 文件中的元素重新排序。通过使用 xsl:copy-of 或 xsl:apply-template 并重新排序元素,我成功地做到了这一点。但是,如果源 xml 有一些 xslt 应用模板中不存在的新元素,那么这些新元素根本不会被复制。就像在下面的示例中一样,元素“状态”在输出中丢失。

例如。原始xml

<Company>
  <Employee id="100" Name="John" >
    <Salary value="15000"/>
    <Qualification text="Engineering">
    <State name="Kerala" code="02">
    <Background text="Indian">
  </Employee>
</Company>

XSLT 按 Qualification 、 Salary 和 Background 的顺序重新排序元素

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output indent="yes"  omit-xml-declaration="yes" method="xml" />
  <xsl:strip-space elements="*"/>

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

  <xsl:template match="Employee">
    <xsl:copy>
      <xsl:apply-templates select="Qualification"/>
      <xsl:apply-templates select="Salary" />
      <xsl:apply-templates select="Background"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

获得的输出

<?xml version="1.0" encoding="utf-8"?>
<Company>
  <Employee>
    <Qualification text="Engineering" />
    <Salary value="15000" />
    <Background text="Indian" />
  </Employee>
</Company>

需要输出

<?xml version="1.0" encoding="utf-8"?>
<Company>
  <Employee>
    <Qualification text="Engineering" />
    <Salary value="15000" />
    <Background text="Indian" />
    <State name="Kerala" code="02"/>
  </Employee>
</Company>

请告诉我 xslt 如何在现有标签的重新排序完成后自动复制任何剩余的新标签。

4

1 回答 1

3
  <xsl:template match="Employee">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates select="Qualification"/>
      <xsl:apply-templates select="Salary" />
      <xsl:apply-templates select="Background"/>
      <xsl:apply-templates select="node()[
         not(self::Qualification | self::Salary | self::Background)]" />
    </xsl:copy>
  </xsl:template>

这将首先复制属性,然后以固定顺序复制您感兴趣的特定元素,最后是除了这三个元素之外的所有其他内容。如果要复制不带属性的标签,请忽略第一个apply-templates(那个)。@*Employee

编辑:您在评论中说您希望将任何评论保留在与其相关的元素之上,即使在重新排序之后也是如此。我能想到的最有效的方法是使用key。在样式表的顶层定义它(在任何模板之外):

<xsl:key name="elementPreamble" match="text()|comment()|processing-instruction()"
      use="generate-id(following-sibling::*[1])" />

给定任何元素节点,这提供了一种有效提取出现在该元素的开始标签和前一个元素的结束标签之间的所有非元素节点的方法(或者这个元素的开始标签和它的父元素的开始标签,如果这是其父元素的第一个子元素)。然后样式表变为:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"  omit-xml-declaration="yes" method="xml" />
  <xsl:strip-space elements="*"/>

  <xsl:key name="elementPreamble" match="text()|comment()|processing-instruction()"
        use="generate-id(following-sibling::*[1])" />

  <xsl:template match="/">
    <!-- Only process the root _element_, not any root-level comments etc.
         Without this template any comments ahead of the root element would be
         doubled in the output -->
    <xsl:apply-templates select="*" />
  </xsl:template>

  <xsl:template match="@*|text()|comment()|processing-instruction()">
    <xsl:copy/>
  </xsl:template>

  <xsl:template match="*">
    <!-- any preamble for this element -->
    <xsl:apply-templates select="key('elementPreamble', generate-id())" />
    <xsl:copy>
      <xsl:apply-templates select="@* | *" />
      <!-- any "post-amble" between the last child element (if any) and this
           element's own closing tag -->
      <xsl:apply-templates select="(text()|comment()|processing-instruction())[
           not(following-sibling::*)]" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Employee">
    <xsl:copy>
      <xsl:apply-templates select="Qualification"/>
      <xsl:apply-templates select="Salary" />
      <xsl:apply-templates select="Background"/>
      <!-- other elements -->
      <xsl:apply-templates select="*[
         not(self::Qualification | self::Salary | self::Background)]" />
      <!-- "post-amble" -->
      <xsl:apply-templates select="(text()|comment()|processing-instruction())[
           not(following-sibling::*)]" />
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

给定输入

<!-- Details of the company's employees -->
<Company>
  <Employee id="100" Name="John" >
    <!-- salary -->
    <!-- check this with HR -->
    <Salary value="15000"/>
    <Qualification text="Engineering"/>
    <!-- the employee's home state -->
    <State name="Kerala" code="02"/>
    <!-- the employee's background -->
    <Background text="Indian"/>
    <!-- this is the end of the record -->
  </Employee>
</Company>

此样式表产生输出

<!-- Details of the company's employees -->
<Company>
  <Employee>
    <Qualification text="Engineering"/>
    <!-- salary -->
    <!-- check this with HR -->
    <Salary value="15000"/>
    <!-- the employee's background -->
    <Background text="Indian"/>
    <!-- the employee's home state -->
    <State name="Kerala" code="02"/>
    <!-- this is the end of the record -->
  </Employee>
</Company>

注释仍然附加到它们最近的后续元素,其顺序与它们在输入文档中出现的顺序相同。

于 2013-07-26T19:29:57.907 回答