Copying remaining elements not present in xslt app

2019-07-23 04:20发布

问题:

I am new to xslt and facing a problem. I have to reorder the elements in an xml file based on a predefined order using xslt. I was successful in doing so by using the xsl:copy-of or xsl:apply-template and reordering the elements. However if the source xml has some new elements not present in the xslt apply template then these new elements are simply not getting copied. Like in the below example the element 'State' is lost in the output.

eg. Original xml

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

XSLT that reorders the elements in the order of Qualification , Salary and 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>

Output Obtained

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

Output Required

<?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>

Please tell me how xslt can copy any remaining new tags automatically after the reordering of the existing tags are done.

回答1:

  <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>

This will first copy the attributes, then the specific elements you're interested in in the fixed order, and finally everything else except those three elements. Leave out the first apply-templates (the @* one) if you want to copy the Employee tag without its attributes.

Edit: you say in your comment that you want to keep any comment above the element to which it pertains, even after re-ordering. The most efficient way I can think to do that would be using a key. Define this at the top level of the stylesheet (outside any templates):

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

Given any element node, this provides a way to efficiently extract all the non-element nodes that occur between the start tag of this element and the end tag of the one before (or this element's start tag and its parent's start tag, if this is the first child element of its parent). The stylesheet then becomes:

<?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>

Given the input

<!-- 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>

this stylesheet produces the output

<!-- 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>

Comments remain attached to their nearest following element, in the same order they appeared in the input document.



标签: xml xslt