Search code examples
xmlxslttransformation

Overwrite character data (untagged text) contained in a specific tag and add to that block another tag, using XSLTPROC


I need to execute XML transformation on some .xml files based on untagged text contained in specific tag. The problem is that although the untagged text is constant, the "activity" node name can vary. Thus, I cannot rely on activity node name, rather I can only rely on the untagged text in the specific tag. The untagged text that I should look for and then replace is:

%%some/Global%%

And the tree of the tags will be always constant and look like this: ProcessDefinition/activity[name that varies]/config/endpointURL/

If I match this combination of tag tree and untagged text, then and only then I should replace the untagged text to:

%%new/Code%%

And add the ssl block under the endpointURL tag(in the config block):

<ns999:ssl xmlns:ns999="http://some/xmlns/target">
    <ns999:strongCipherSuitesOnly>true</ns999:strongCipherSuitesOnly>
    <ns999:cert isRef="true">/.folder</ns999:cert>
</ns999:ssl>

In general speaking ...

The input:

<pd:ProcessDefinition xmlns:pfx6="SOME DATA HERE">
    <pd:activity name="THIS NAME VARIES FROM EACH PROJECT TO PROJECT">
        <pd:x>00</pd:x>
        <pd:y>00</pd:y>
        <config>
            <endpointURL>%%some/Global%%</endpointURL>
        </config>
        <pd:inputBindings>
            SOME UNIMPORTANT DATA
        </pd:inputBindings>
    </pd:activity>
</pd:ProcessDefinition>

The desired output:

<pd:ProcessDefinition xmlns:pfx6="SOME DATA HERE">
    <pd:activity name="THIS NAME VARIES FROM EACH PROJECT TO PROJECT">
        <pd:x>00</pd:x>
        <pd:y>00</pd:y>
        <config>
            <endpointURL>%%new/Code%%</endpointURL>
            <ns999:ssl xmlns:ns999="http://some/xmlns/target">
                <ns999:strongCipherSuitesOnly>true</ns999:strongCipherSuitesOnly>
                <ns999:cert isRef="true">/.folder</ns999:cert>
            </ns999:ssl>
        </config>
        <pd:inputBindings>
            SOME UNIMPORTANT DATA
        </pd:inputBindings>
    </pd:activity>
</pd:ProcessDefinition>

I have tried the following:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:ns999="http://some/xmlns/test_2"
    xmlns="http://some/xmlns/test_1" 
    xmlns:tc="http://some/xmlns/test_1" exclude-result-prefixes="tc">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="endpointURL">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
            <ns999:ssl xmlns:ns999="http://some/xmlns/target">
                <ns999:strongCipherSuitesOnly>true</ns999:strongCipherSuitesOnly>
                <ns999:cert isRef="true">/.folder</ns999:cert>
            </ns999:ssl>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

I cannot find how to filter the endpointURL by untagged text %%some/Global%%? And, if the ssl node injection will be in the right place after that tag?

Please, any help would be appreciated.


Solution

  • It sounds as if you want something like

    <xsl:stylesheet version="1.0" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        xmlns:ns999="http://some/xmlns/test_2"
        xmlns="http://some/xmlns/test_1" 
        xmlns:tc="http://some/xmlns/test_1" exclude-result-prefixes="tc">
        
        <xsl:output method="xml" indent="yes"/>
        
        <xsl:template match="node()|@*" name="identity">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
        
        <xsl:template match="endpointURL[. = '%%some/Global%%']">
            <xsl:call-template name="identity"/>
            <ns999:ssl xmlns:ns999="http://some/xmlns/target">
                <ns999:strongCipherSuitesOnly>true</ns999:strongCipherSuitesOnly>
                <ns999:cert isRef="true">/.folder</ns999:cert>
            </ns999:ssl>
        </xsl:template>
        
    </xsl:stylesheet>
    

    Perhaps change <xsl:template match="endpointURL[. = '%%some/Global%%']"> to <xsl:template match="endpointURL[contains(., '%%some/Global%%')]"> if the value is only meant to be part of the endpointURL.

    Online fiddle using libxslt/lxml in the browser.

    Michael Kay spotted that your sample data in the question suggests a change to the endpointURL so it might well be that you want

    <xsl:stylesheet version="1.0" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        xmlns:ns999="http://some/xmlns/test_2"
        xmlns="http://some/xmlns/test_1" 
        xmlns:tc="http://some/xmlns/test_1" exclude-result-prefixes="tc">
        
        <xsl:output method="xml" indent="yes"/>
        <xsl:strip-space elements="*"/>
        
        <xsl:template match="node()|@*" name="identity">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
        
        <xsl:template match="endpointURL[. = '%%some/Global%%']">
            <xsl:copy>%%new/Code%%</xsl:copy>
            <ns999:ssl xmlns:ns999="http://some/xmlns/target">
                <ns999:strongCipherSuitesOnly>true</ns999:strongCipherSuitesOnly>
                <ns999:cert isRef="true">/.folder</ns999:cert>
            </ns999:ssl>
        </xsl:template>
        
    </xsl:stylesheet>
    

    Online fiddle.