简体   繁体   中英

XSLT to move XML sibling to child

Let me start by saying I have no idea what I'm doing with XSLT. All of the XSLT I have I inherited from other people. I have some XML (it's EAD if that helps) that is not formed the way our stylesheets expect it to be and therefore it will not properly transform into XHTML.

Basically, I need for the <unitdate> to be the child of the <unittitle> , not its sibling.

Most of the document looks like this:

<c03 id="ref13" level="file">
<did>
<unittitle>1. President (White House)</unittitle>
<container id="cid192710" type="Box" label="Text">1</container>
<container parent="cid192710" type="Folder">2</container>
<unitdate normal="1953/1956" type="inclusive">1953-1956</unitdate>
</did>
</c03>

And I need it to look like this:

<c03 id="ref13" level="file">
<did>
<unittitle>1. President (White House)<unitdate normal="1953/1956" type="inclusive">1953-1956</unitdate></unittitle>
<container id="cid192710" type="Box" label="Text">1</container>
<container parent="cid192710" type="Folder">2</container>
</did>
</c03>

Is there a simple way to do this? I know there are similar questions, but I don't understand this well enough to adapt them to make it work properly. Thanks.

Try these templates:

<xsl:template match="unittitle[following-sibling::unitdate]">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
    <xsl:copy-of select="following-sibling::unitdate"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="unitdate"/>

And the identity template:

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

The identity template copies everything exactly as-is. The two previous templates override it in the specific cases where you have a unittitle element with a unitdate sibling following it, and the unitdate element itself.

You'll probably notice the first template is almost identical to the identity template- this is because it copies the unittitle the same way the identity one does, except it also copies the following unitdate element as well, after processing everything else (ie the text).

The one-line unitdate template simply removes it from where it was by handling it, and not outputting anything.

To not modify the existing XSLT's and still be able to process the incxoming XML you can use the following XSLT (1.0) segment to solve your problem.

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

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

  <xsl:template match="did">
      <xsl:copy>
         <xsl:apply-templates select="*[local-name()!='unitdate']"/>
      </xsl:copy>
  </xsl:template>

   <xsl:template match="unittitle">
       <xsl:element name="unittitle">
             <xsl:value-of select="text()"/>
             <xsl:copy-of select="../unitdate" />
       </xsl:element>
  </xsl:template>

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

</xsl:stylesheet>

But your should seriously consider NOT using this. Modifying the existing XSLT's is the reasonable thing to do. This only produces performance overhead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM