简体   繁体   中英

Move/Copy node to multiple child nodes with XSLT

I'm completely new to XSLT and need some help to solve one of my issues. What I want to accomplish is this:

I have a file looking something like this:

<Transaction>
    <Date>2010-10-14T12:06:12.164+01:00</Date>
    <Production>NO</Production>
    <Document fun:OID="1.9.101106">
        <DocumentType xmlns="">Monthly A</DocumentType>
        <RangeName xmlns="">Range Name</RangeName>
        <Name xmlns="">Equity</Name>
        <Language xmlns="">English</Language>
        <Class xmlns="">A Acc</Class>
        <Active xmlns="">YES</Active>
        <Country xmlns="">UK</Country>
        <Country xmlns="">Luxembourg</Country>
        <Country xmlns="">Denmark</Country>
        <Country xmlns="">Malta</Country>
        <Primary fun1:OID="1.9.101106" xmlns="" xmlns:fun1="DocumentXML.com">
              <Name>SISF-Indian-Equity-A-Acc-FMR-UKEN</Name>
              <FileSizeInKB>176784</FileSizeInKB>
              <FileType>pdf</FileType>
              <ReportingPeriod>September</ReportingPeriod>
              <ReportingYear>2010</ReportingYear>
        </Primary>
        <Primary fun1:OID="1.9.101118" xmlns="" xmlns:fun1="DocumentXML.com">
              <Name>SISF-Indian-Equity-A-Acc</Name>
              <FileSizeInKB>176784</FileSizeInKB>
              <FileType>pdf</FileType>
              <ReportingPeriod>September</ReportingPeriod>
              <ReportingYear>2010</ReportingYear>
        </Primary>
    </Document>
</Transaction>

And I want to keep the current file, except that I want to copy the country nodes in to be in the Primary node. So it would look something like this:

<Transaction>
    <Date>2010-10-14T12:06:12.164+01:00</Date>
    <Production>NO</Production>
    <Document fun:OID="1.9.101106">
        <DocumentType xmlns="">Monthly A</DocumentType>
        <RangeName xmlns="">Range Name</RangeName>
        <Name xmlns="">Equity</Name>
        <Language xmlns="">English</Language>
        <Class xmlns="">A Acc</Class>
        <Active xmlns="">YES</Active>
        <Country xmlns="">UK</Country>
        <Country xmlns="">Luxembourg</Country>
        <Country xmlns="">Denmark</Country>
        <Country xmlns="">Malta</Country>
        <Primary fun1:OID="1.9.101106" xmlns="" xmlns:fun1="DocumentXML.com">
              <Name>SISF-Indian-Equity-A-Acc-FMR-UKEN</Name>
              <FileSizeInKB>176784</FileSizeInKB>
              <FileType>pdf</FileType>
              <ReportingPeriod>September</ReportingPeriod>
              <ReportingYear>2010</ReportingYear>
              <Country xmlns="">UK</Country>
              <Country xmlns="">Luxembourg</Country>
              <Country xmlns="">Denmark</Country>
              <Country xmlns="">Malta</Country>
        </Primary>
        <Primary fun1:OID="1.9.101118" xmlns="" xmlns:fun1="DocumentXML.com">
              <Name>SISF-Indian-Equity-A-Acc</Name>
              <FileSizeInKB>176784</FileSizeInKB>
              <FileType>pdf</FileType>
              <ReportingPeriod>September</ReportingPeriod>
              <ReportingYear>2010</ReportingYear>
              <Country xmlns="">UK</Country>
              <Country xmlns="">Luxembourg</Country>
              <Country xmlns="">Denmark</Country>
              <Country xmlns="">Malta</Country>
        </Primary>
    </Document>
</Transaction>

What would the best approach be to accomplish that? Do I need to do a copy of the whole document first and then copy the individual countries, or can I do this in one go somehow?

The trick is to use an identity template to copy the whole document but still be able to modify the parts you want:

With this (slightly modified) input:

<?xml version="1.0" encoding="UTF-8"?>
<Transaction xmlns:fun1="DocumentXML.com">
  <Date>2010-10-14T12:06:12.164+01:00</Date>
  <Production>NO</Production>
  <Document fun1:OID="1.9.101106">
    <DocumentType xmlns="">Monthly A</DocumentType>
    <RangeName xmlns="">Range Name</RangeName>
    <Name xmlns="">Equity</Name>
    <Language xmlns="">English</Language>
    <Class xmlns="">A Acc</Class>
    <Active xmlns="">YES</Active>
    <Country xmlns="">UK</Country>
    <Country xmlns="">Luxembourg</Country>
    <Country xmlns="">Denmark</Country>
    <Country xmlns="">Malta</Country>
    <Primary fun1:OID="1.9.101106" xmlns="" xmlns:fun1="DocumentXML.com">
      <Name>SISF-Indian-Equity-A-Acc-FMR-UKEN</Name>
      <FileSizeInKB>176784</FileSizeInKB>
      <FileType>pdf</FileType>
      <ReportingPeriod>September</ReportingPeriod>
      <ReportingYear>2010</ReportingYear>
    </Primary>
    <Primary fun1:OID="1.9.101118" xmlns="" xmlns:fun1="DocumentXML.com">
      <Name>SISF-Indian-Equity-A-Acc</Name>
      <FileSizeInKB>176784</FileSizeInKB>
      <FileType>pdf</FileType>
      <ReportingPeriod>September</ReportingPeriod>
      <ReportingYear>2010</ReportingYear>
    </Primary>
  </Document>
</Transaction>

Explanation for the changes in input XML: The supplied input XML isn't valid because you use double quotes for every attribute. Also, the fun prefix isn't declared for the Document node. I've changed the fun prefix to fun1 and bound the prefix at root level.


And this stylesheet:

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

  <xsl:output indent="yes"/>

  <!-- This identity template copies the document -->
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>

  <!-- This template will only match the 'Primary' nodes
       and modify them the way you want. -->
  <xsl:template match="Primary">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
      <!-- As you can see, this is the only difference
           between the identity template and this specific
           template. -->
      <xsl:copy-of select="../Country"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Will give you an identical document but with Country copied into Primary :

<?xml version="1.0" encoding="UTF-8"?>
<Transaction xmlns:fun1="DocumentXML.com">
  <Date>2010-10-14T12:06:12.164+01:00</Date>
  <Production>NO</Production>
  <Document fun1:OID="1.9.101106">
      <DocumentType>Monthly A</DocumentType>
      <RangeName>Range Name</RangeName>
      <Name>Equity</Name>
      <Language>English</Language>
      <Class>A Acc</Class>
      <Active>YES</Active>
      <Country>UK</Country>
      <Country>Luxembourg</Country>
      <Country>Denmark</Country>
      <Country>Malta</Country>
      <Primary fun1:OID="1.9.101106">
         <Name>SISF-Indian-Equity-A-Acc-FMR-UKEN</Name>
         <FileSizeInKB>176784</FileSizeInKB>
         <FileType>pdf</FileType>
         <ReportingPeriod>September</ReportingPeriod>
         <ReportingYear>2010</ReportingYear>
         <Country>UK</Country>
         <Country>Luxembourg</Country>
         <Country>Denmark</Country>
         <Country>Malta</Country>
      </Primary>
      <Primary fun1:OID="1.9.101118">
         <Name>SISF-Indian-Equity-A-Acc</Name>
         <FileSizeInKB>176784</FileSizeInKB>
         <FileType>pdf</FileType>
         <ReportingPeriod>September</ReportingPeriod>
         <ReportingYear>2010</ReportingYear>
         <Country>UK</Country>
         <Country>Luxembourg</Country>
         <Country>Denmark</Country>
         <Country>Malta</Country>
      </Primary>
  </Document>
</Transaction>

Just for fun, the shortest stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
            <xsl:apply-templates select="self::Primary/../Country"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Other shorter without changing the identity rule:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Primary/*[last()]">
        <xsl:call-template name="identity"/>
        <xsl:apply-templates select="../../Country"/>
    </xsl:template>
</xsl:stylesheet>

Note : Both don't use xsl:copy-of but xsl:apply-templates . This allows further processing.

well if you are using xslt to generate a new xml doccument i would just create the doccument then for the primary node do this

<xsl:for-each select="../Country">
<Country><xsl:value-of select="."/></Country>
</xsl:for-each>

there may be simpler ways to achieve the same results..?

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