0

Looking to merge the following XML. I'd like to this in Python, though it's not a requirement by any means.

File 1:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types> 
        <members>Class 1</members> 
        <members>Class 2</members>    
        <name>ApexClass</name>
    </types>
    <types>
        <members>Trigger 1</members>
        <name>ApexTrigger</name>
    </types>
    <types>
        <members>Rule 1</members>
        <members>Rule 2</members>
        <name>WorkflowRule</name>
    </types>   
    <types>
        <members>Address</members>
        <name>CustomField</name>
    </types>     
    <version>39.0</version>
</Package>

File 2:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Class 3</members>  
        <name>ApexClass</name>
    </types>
    <types>
        <members>Rule 2</members>
        <name>WorkflowRule</name>
    </types>  
    <types>
        <members>Phone</members>
        <name>CustomField</name>
    </types>     
    <version>41.0</version>
</Package>

Merge File 1 and File 2 to create File 3 below:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Class 1</members> 
        <members>Class 2</members>    
        <members>Class 3</members>    
        <name>ApexClass</name>
    </types>
    <types>
        <members>Trigger 1</members>
        <name>ApexTrigger</name>
    </types>
    <types>
        <members>Rule 1</members>
        <members>Rule 2</members>
        <name>WorkflowRule</name>
    </types>  
    <types>
        <members>Address</members>
        <members>Phone</members>
        <name>CustomField</name>
    </types>     
    <version>41.0</version>
</Package>

Please note the identifying node can be one of more than 50 different values.

Thanks in advance for any assistance.

Edit: Ouch, that down vote hurts. To clarify & justify the question, I should have pointed out that I was having difficulty on where to even start on this problem as extensive searches on SO were yielding no clues, and that the point of the question was to get suggestions on where to begin.

3
  • Yes you can do this. In python 3.6 at least the xml handler can read the whole file into a nice object and duplicated get turned into array, then you can just combine the arrays pretty easy. Commented Oct 11, 2018 at 5:20
  • @mpaler, please ignore the close vote and downvote on this question. There is absolutely nothing wrong with the question, there is something seriously wrong with the person who downvoted it. Certainly the close reason ("too broad") is nonsense. One could criticise the question because you don't show what you've already tried, but in my view it's perfectly reasonable to ask what approach to take if you don't know where to start. Commented Oct 11, 2018 at 8:04
  • @MichaelKay thanks. I figured not even showing an attempt would be a strike against -- however, you are correct, I wasn't even sure where to start. Perhaps I should have clarified that in the question... Commented Oct 11, 2018 at 16:12

1 Answer 1

1

Since you're prepared to consider alternatives to Python coding, here's an XSLT 3.0 solution:

<xsl:transform version="3.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    xpath-default-namespace="http://soap.sforce.com/2006/04/metadata"
    expand-text="true">
    <xsl:param name="file1" as="xs:string"/>
    <xsl:param name="file2" as="xs:string"/>
    <xsl:template name="xsl:initial-template">
        <Package xmlns="http://soap.sforce.com/2006/04/metadata">
            <xsl:merge>
                <xsl:merge-source for-each-source="$file1, $file2"
                    select="//types" sort-before-merge="true">
                    <xsl:merge-key select="name"/>
                </xsl:merge-source>
                <xsl:merge-action>
                    <types>
                        <xsl:for-each-group select="current-merge-group()/members"
                            group-by=".">
                            <xsl:copy-of select="current-group()[1]"/>
                        </xsl:for-each-group>
                        <name>{current-merge-key()}</name>
                    </types>
                </xsl:merge-action>
            </xsl:merge>
        </Package>
    </xsl:template>
</xsl:transform>

You can run this from the command line as follows:

java net.sf.saxon.Transform -xsl:test.xsl -t -it file1=file1.xml file2=file2.xml !indent=yes

Explanation: the xsl:merge-source identifies the two sequences of elements to be merged; xsl:merge-key defines the key on which they are to be merged, and tells us that the sequences are not already sorted by that key. The xsl:merge-action says how each group of elements sharing a merge key is to be processed; in this case we output the distinct <members> elements using xsl:for-each-group, then we output the <name>.

There's one difference between the output and your desired output: the stylesheet produces output sorted by <name>. I'm not sure what your criteria for sorting the output were; if the output order is important, then we might need adjustments.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank Michael! Frankly, I never realized xsl was such a slick tool. How would I implement this solution from the command line? And can you suggest a way to pass in for-each-source="doc('1.xml'), doc('2.xml')" as a command-line argument?
Download an XSLT 3.0 processor such as Saxon-HE and follow the instructions at saxonica.com/documentation/index.html#!using-xsl/commandline
I've amended the example to declare the filenames as parameters. It can also be generalised to handle any number of input files if you want.
Hi Michael, giving this a good go today and am running into the following: Static error in xsl:merge-key/@sort-before-merge on line 15 column 66 of stylesheet.xsl: XTSE0090: Attribute @sort-before-merge is not allowed on element <xsl:merge-key> I should point out that I'm running OSX version of saxon: Saxon-HE 9.9.0.1J from Saxonica
Sorry I put the attribute on the wrong element: it belongs on xsl:merge-source. Since you're interested in using it, I'll now test the example.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.