Home > Enterprise >  Efficient mapping of alike elements in different namespaces (XSLT 1.0)
Efficient mapping of alike elements in different namespaces (XSLT 1.0)

Time:12-15

I need to translate XML->XML for several input documents that have "embedded" documents with different namespaces. These embedded documents have elements of the same type (i.e. based on same xsd) that i'd like to map in a generic way / template into the output document - regardless of the namespace.

Simple example Input XML:

<?xml version="1.0" encoding="UTF-8"?>
<Message xmnls="mymessage">
    <Header>...</Header>
    <Body xmlns="type1">
        <DocumentType1>
            <Creditor>
                <Name>Mr Creditor</Name>
                <Address>Muddy Creek 42</Address>
            </Creditor>
            <CreditorAccount>
                <Id>794115296</Id>
            </CreditorAccount>
        </DocumentType1>
    </Body>
</Message>

Example Input XML 2:

<?xml version="1.0" encoding="UTF-8"?>
<Message xmnls="mymessage">
    <Header>...</Header>
    <Body xmlns="type2"><!-- different namespace -->
        <DocumentType2>
            <Creditor><!-- same element -->
                <Name>Mr Creditor</Name> 
                <Address>Muddy Creek 42</Address>
            </Creditor>
            <CreditorAccount>
                <Id>794115296</Id>
            </CreditorAccount>
        </DocumentType2>
    </Body>
</Message>

For both examples, i'd like to extract the "Creditor" information which is structured the same way. But as you see, Body-namespace ("type1", "type2"...) and child-Element name ("DocumentType1",...) varies, i.e. the whole name, not only the number at the end.

Output in both cases should be like:

<?xml version="1.0"?>
<Request>
    <Creditor>
        <Name>Mr Creditor</Name>
        <Address>Muddy Creek 42</Address>
        <Account>794115296</Account>
    </Creditor>
</Request>

How can i map the Creditor information for both Messages-Examples in the Output format the best way with XSLT 1.0 / libxml exslt? Regrettably I'm bound to 1.0.

I can use "wildcards" for sure, but i fear that might decrease performance. There will be a lot of mappings like this in the XSLT and more input data than the examples. Do you have expierences with that regarding performance?

Otherwise i would need a template for each different namespace, right? As I understood, there is no way to dynamically set an alias for a namespace in XSLT.

Here is an example XSLT using 'wildcards':

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:mymessage="urn:mymessage"
                exclude-result-prefixes="xsi xsl mymessage" 
                version="1.0">
    
    <xsl:template match="//mymessage:Message">
    <Request>
        <xsl:apply-templates/>
    </Request>
    </xsl:template>
    
    <xsl:template match="mymessage:Header"/>

    <xsl:template match="*[local-name()='Body']">
        <Creditor>
            <Name>
                <xsl:value-of select="*/*[local-name()='Creditor']/*[local-name()='Name']"/>
            </Name>
            <Address>
                <xsl:value-of select="*/*[local-name()='Creditor']/*[local-name()='Address']"/>
            </Address>
            <Account>
                <xsl:value-of select="*/*[local-name()='CreditorAccount']/*[local-name()='Id']"/>
            </Account>
        </Creditor>
    </xsl:template>
</xsl:stylesheet>

Appreciate your help! :)

CodePudding user response:

Can't comment on libxslt performance - but have you tried it? Write your code to be readable and maintainable, and only change it if that gives you a measurable performance problem.

An alternative approach when you want to handle input in multiple namespaces is to do two passes: the first pass strips out (or normalizes) the namespaces, the second pass then handles elements in a uniform namespace. For the first pass, just use <xsl:element name="{local-name()}">

Obviously XSLT 2.0 gives you the partial wildcard syntax *:local which makes this kind of code much more readable.

  • Related