不同名称空间中相似元素的高效映射 (XSLT 1.0)

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

我需要翻译 XML->XML 多个输入文档,这些文档“嵌入”了具有不同命名空间的文档。这些嵌入式文档具有相同类型的元素(即基于相同的 xsd),我想以通用方式/模板将其映射到输出文档中 - 无论命名空间如何。

简单示例输入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>

示例输入 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>

对于这两个示例,我想提取结构相同的“债权人”信息。 但正如您所见,正文命名空间(“type1”、“type2”...)和子元素名称(“DocumentType1”,...)各不相同,即整个名称,而不仅仅是末尾的数字。

两种情况下的输出应该是这样的:

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

我如何才能以 XSLT 1.0/libxml+exslt 的最佳方式映射输出格式中两个消息示例的贷方信息?遗憾的是我绑定了1.0.

我当然可以使用“通配符”,但我担心这会降低性能。 XSLT 中将有很多这样的映射,并且输入数据比示例更多。你有关于性能的经验吗?

否则我需要为每个不同的命名空间创建一个模板,对吗?据我了解,无法在 XSLT 中动态设置名称空间的别名。

这是一个使用 'wildcards' 的 XSLT 示例:

<?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>

感谢您的帮助! :)

无法评论 libxslt 性能 - 但您尝试过吗?编写可读和可维护的代码,并且只有在给您带来可衡量的性能问题时才更改它。

当您想要处理多个命名空间中的输入时,另一种方法是进行两次传递:第一次传递去除(或规范化)命名空间,然后第二次传递处理统一命名空间中的元素。对于第一遍,只需使用 <xsl:element name="{local-name()}">

显然,XSLT 2.0+ 为您提供了部分通配符语法 *:local,这使得此类代码更具可读性。