【问题标题】:不同名称空间中相似元素的有效映射 (XSLT 1.0)
【发布时间】:2025-12-08 04:15:01
【问题描述】:

我需要为几个具有不同命名空间的“嵌入”文档的输入文档翻译 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>

对于这两个示例,我想提取结构相同的“贷方”信息。 但正如您所见,Body-namespace ("type1", "type2"...) 和 child-Element name ("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 中为命名空间动态设置别名。

这是一个使用“通配符”的 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>

感谢您的帮助! :)

【问题讨论】:

  • 您是否有Body 及其后代可能位于的所有可能命名空间的列表?
  • @michael.hor257k:命名空间实际上是 ISO20022 消息类型,例如“pacs.008.001.08”、“pacs.004.001.09”、“camt.053.001.08”等。我将从一组比如说 5 种消息类型开始,但以后可能会增加。所以是的,我有一个完整的列表,但就未来的改编而言,它并不详尽。
  • @BusyBeaver 有人在那里做了一个非常糟糕的设计决定。我敢肯定他们认为这很聪明,但他们显然从未做过任何认真的 XML 编程。
  • @MichaelKay 我同意... imo 这个“标准”被每个组使用它自己的一组消息和自己的版本分成几部分(版本是命名空间的一部分,如 *. 08, *.09 ...) 并引入细微的变化以满足他们的要求。构建财务信息的意图是好的,但最终它隐藏在一堆“方言”中,导致语言混乱。 ;)

标签: xml xslt xslt-1.0


【解决方案1】:

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

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

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

【讨论】:

  • 我没有对性能进行深入测试。问题是,我刚刚使用您提到的部分通配符语法首先使用了 XSLT 2.0,它运行良好。然后我不得不重新编码它以与 XSLT 1.0 兼容,而我的“IDE”Altova XML Spy 只是挂起,需要第 10 次。但是我发现您对两次运行的回答非常有帮助,我想如果性能真的这么差,我可以这样做。谢谢!
  • 只是为了澄清——我使用 Altova XML Spy 进行了 XSLT 2.0 的第一个实现,但后来需求发生了变化,因此它最终必须在带有 XSLT 1.0 的 libxml 实例上运行......跨度>
最近更新 更多