简体   繁体   中英

add top level tag once while merging xml files in xslt

I'm trying to merge multiple xml files in specific folder using xslt and saxon-HE 9.9.1 .NET. I need to create a generic merger so that I will not add a static tag inside the template to use it with different nodes' names, I tried to make a loop to add the root or the top level tag once in the beginning but it also close the tag before the xml end but there is an issue with the top level tag Example: XML File1:

<Arr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <Src>
    <name>C</name>
    <pr>pr</pr>
    <par>
      <Q>
        <isC>false</isC>
        <name>hrh1</name>
        <type>xx1</typ>
      </Q>
    </par>
    <st />
 </Src>
 <Src>
    <name>C</name>
    <pr></pr>
    <par>
      <Q>
        <isC>false</isC>
        <name>hrh2</name>
        <type>xx2</typ>
      </Q>
    </par>
    <st>
      <Src>
        <name>st1</name>
        <pr>prst1</pr>
        <par>
          <Q>
            <isC>false</isC>
            <name>q1</name>
            <type>t1</type>
          </Q>
          <Q>
            <isC>false</isC>
            <name>q2</name>
            <type>t2</type>
          </Q>
        </par>
        <st />
      </Src>
    </st>
  </Src>
 </Arr>

XML File2:

<Arr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <Src>
    <name>CFile2</name>
    <pr>C2</pr>
    <par>
      <Q>
        <isC>false</isC>
        <name>hrh1</name>
        <type>xx1</typ>
      </Q>
    </par>
    <st />
 </Src>
 </Arr>

expected output:

<Arr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <Src>
    <name>C</name>
    <pr>pr</pr>
    <par>
      <Q>
        <isC>false</isC>
        <name>hrh1</name>
        <type>xx1</typ>
      </Q>
    </par>
    <st />
 </Src>
 <Src>
    <name>C</name>
    <pr></pr>
    <par>
      <Q>
        <isC>false</isC>
        <name>hrh2</name>
        <type>xx2</typ>
      </Q>
    </par>
    <st>
      <Src>
        <name>st1</name>
        <pr>prst1</pr>
        <par>
          <Q>
            <isC>false</isC>
            <name>q1</name>
            <type>t1</type>
          </Q>
          <Q>
            <isC>false</isC>
            <name>q2</name>
            <type>t2</type>
          </Q>
        </par>
        <st />
      </Src>
    </st>
  </Src>
  <Src>
    <name>CFile2</name>
    <pr>C2</pr>
    <par>
      <Q>
        <isC>false</isC>
        <name>hrh1</name>
        <type>xx1</typ>
      </Q>
    </par>
    <st />
  </Src>
 </Arr>

my current template which is produces wrong output because of the top level tag closure

<?xml version="1.0" encoding="windows-1256"?>   
<xsl:stylesheet version="3.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:user="http://tempuri.org/msxsl" >     
 <xsl:output method="xml" indent="yes" encoding="windows-1256" />

<xsl:template name="main">
 
 <xsl:for-each select="collection('.?select=*.xml')/*">
  <xsl:choose>
        <xsl:when test="position() = 1">
          <xsl:copy>
        <xsl:copy-of select="/*/node()"/> 
        </xsl:copy>
   
        </xsl:when>
        <xsl:otherwise>
         <xsl:copy-of select="/*/node()"/> 
        </xsl:otherwise>
      </xsl:choose>
      
</xsl:for-each> 

</xsl:template>

</xsl:stylesheet>

I run from the cmd with the following command:

transform -it:main -xsl:merge_xml.xslt -o:output.xml

my current output which is wrong

<Arr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <Src>
    <name>C</name>
    <pr>pr</pr>
    <par>
      <Q>
        <isC>false</isC>
        <name>hrh1</name>
        <type>xx1</typ>
      </Q>
    </par>
    <st />
 </Src>
 <Src>
    <name>C</name>
    <pr></pr>
    <par>
      <Q>
        <isC>false</isC>
        <name>hrh2</name>
        <type>xx2</typ>
      </Q>
    </par>
    <st>
      <Src>
        <name>st1</name>
        <pr>prst1</pr>
        <par>
          <Q>
            <isC>false</isC>
            <name>q1</name>
            <type>t1</type>
          </Q>
          <Q>
            <isC>false</isC>
            <name>q2</name>
            <type>t2</type>
          </Q>
        </par>
        <st />
      </Src>
    </st>
  </Src>
</Arr>
  <Src>
    <name>CFile2</name>
    <pr>C2</pr>
    <par>
      <Q>
        <isC>false</isC>
        <name>hrh1</name>
        <type>xx1</typ>
      </Q>
    </par>
    <st />
  </Src>

I would simply use

 <xsl:param name="uris" select="uri-collection('.?select=*.xml')"/>

 <xsl:template name="xsl:initial-template">
    <xsl:copy select="$uris => head() => doc()/*">
      <xsl:copy-of select="($uris ! doc(.))/*/node()"/>
    </xsl:copy>
  </xsl:template>

To be able to switch to streaming if Saxon EE is an option one could try

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all" expand-text="yes">

    <xsl:function name="mf:merge" as="document-node()">
        <xsl:param name="uris"/>
        <xsl:source-document href="{head($uris)}" streamable="yes">
            <xsl:document>
                <xsl:copy select="*">
                    <xsl:copy-of select="node()"/>
                    <xsl:iterate select="tail($uris)">
                        <xsl:source-document href="{.}" streamable="yes">
                            <xsl:copy-of select="*/node()"/>
                        </xsl:source-document>
                    </xsl:iterate>
                </xsl:copy>
            </xsl:document>
        </xsl:source-document>
    </xsl:function>

    <xsl:output method="xml" indent="yes"/>

    <xsl:template name="xsl:initial-template">
        <xsl:sequence select="mf:merge(uri-collection('?select=*.xml'))"/>
    </xsl:template>

</xsl:stylesheet>

Martin's solution will work, but I would do:

  <xsl:template name="xsl:initial-template">
    <xsl:variable name="coll" select="collection('.?select=*.xml')"/>
    <xsl:copy select="$coll[1]/*">
      <xsl:copy-of select="$coll/*/*"/>
    </xsl:copy>
  </xsl:template>

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