0

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>
Yara
  • 1
  • 1

2 Answers2

0

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 Honnen
  • 160,499
  • 6
  • 90
  • 110
  • thank you for answering, I am working with Saxon HE the first solution may work but it gives me an error in A sequence of more than one item is not allowed as the first argument of fn:doc() I also have a question head() function gave me the top of the document right? how did you reach this information I'm just asking for knowledge exchange – Yara Aug 31 '20 at 19:10
  • @Yara, see the edit, I had used the wrong operator. As for the definitions of functions, I suggest you start with https://www.w3.org/TR/xpath-functions// – Martin Honnen Aug 31 '20 at 19:24
  • https://maxtoroq.github.io/xpath-ref/ might also help, it is compiled from the spec but perhaps easier to navigate. – Martin Honnen Aug 31 '20 at 19:26
0

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>
Michael Kay
  • 156,231
  • 11
  • 92
  • 164