【问题标题】:How to merge elements between two documents using XSLT 2.0?如何使用 XSLT 2.0 合并两个文档之间的元素?
【发布时间】:2016-12-14 05:11:58
【问题描述】:

我在一个 XML 文档中有如下默认配置项:

<ProgramConfig>

    <Fragment xml:lang="en" name="TargetSector">fragment/target_sector.xdp</Fragment>
    <Fragment xml:lang="fr" name="TargetSector">fragment/target_sector_fr.xdp</Fragment>


    <MasterTemplate xml:lang="en">master/default_en.xdp</MasterTemplate>
    <MasterTemplate xml:lang="fr">master/default_fr.xdp</MasterTemplate>

</ProgramConfig>

特定程序可以覆盖默认配置,例如:

<ProgramConfig>

    <Fragment xml:lang="en" name="TargetSector">fragment/1-5ABQ/target_sector.xdp</Fragment>

    <MasterTemplate xml:lang="fr">master/default_fr_1-5ABQ.xdp</MasterTemplate>

</ProgramConfig>

我需要合并XML文档,这样输出就变成了:

<ProgramConfig>

    <Fragment xml:lang="en" name="TargetSector">fragment/1-5ABQ/target_sector.xdp</Fragment>
    <Fragment xml:lang="fr" name="TargetSector">fragment/target_sector_fr.xdp</Fragment>


    <MasterTemplate xml:lang="en">master/default_en.xdp</MasterTemplate>
    <MasterTemplate xml:lang="fr">master/default_fr_1-5ABQ.xdp</MasterTemplate>

</ProgramConfig>

如果程序特定的 XML 有一个与默认 XML 具有相同名称和匹配属性的元素,那么它应该替换输出文档中的值。

XML 非常扁平 - 它始终是 ProgramConfig 根目录下的一组元素,没有其他子元素。每个元素都定义了资产的文件系统路径。

有没有办法使用 XSLT 做到这一点。我尝试使用文档功能,但我不确定如何匹配元素和所有属性。

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    使用 XSLT 3.0,您可以使用 for-each-group 和使用节点名称和所有属性的复合分组键来解决它:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:math="http://www.w3.org/2005/xpath-functions/math"
        exclude-result-prefixes="xs math"
        version="3.0">
    
        <xsl:output indent="yes"/>
    
        <xsl:template name="xsl:initial-template">
            <ProgramConfig>
                <xsl:for-each-group select="doc('defaultConfig.xml')/ProgramConfig/*,
                                            doc('overrideConfig.xml')/ProgramConfig/*"
                                            group-by="node-name(.), sort(@*, function($a) { name($a) })" composite="yes">
                    <xsl:copy-of select="if (current-group()[2]) then current-group()[2] else current-group()[1]"/>
                </xsl:for-each-group>
            </ProgramConfig>
        </xsl:template>
    
    </xsl:stylesheet>
    

    使用 XSLT 2.0,根据名称和所有属性值构造单个分组键有点困难:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:math="http://www.w3.org/2005/xpath-functions/math"
        xmlns:mf="http://example.com/mf"
        exclude-result-prefixes="xs math mf"
        version="2.0">
    
        <xsl:output indent="yes"/>
    
        <xsl:function name="mf:sort" as="node()*">
            <xsl:param name="input-nodes" as="node()*"/>
            <xsl:perform-sort select="$input-nodes">
                <xsl:sort select="name()"/>
            </xsl:perform-sort>
        </xsl:function>
    
        <xsl:template name="main">
            <ProgramConfig>
                <xsl:for-each-group select="doc('defaultConfig.xml')/ProgramConfig/*,
                    doc('overrideConfig.xml')/ProgramConfig/*"
                    group-by="string-join((string(node-name(.)), mf:sort(@*)), '|')">
                    <xsl:copy-of select="if (current-group()[2]) then current-group()[2] else current-group()[1]"/>
                </xsl:for-each-group>
            </ProgramConfig>
        </xsl:template>
    
    </xsl:stylesheet>
    

    【讨论】:

    • 太棒了。谢谢你。一个问题 - node()* 的 as 属性是什么意思?我不确定它是如何匹配属性的,我认为它只会匹配元素。
    • 根据w3.org/TR/xpath20/#id-sequencetype-syntaxas属性中使用的序列类型,“任何类型的测试”node()表示任何类型的节点和属性都是数据模型中的节点。它在 XSLT 模式中有所不同,其中w3.org/TR/xslt20/#pattern-examples 定义“node() 匹配除属性节点、命名空间节点或文档节点之外的任何节点”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-19
    • 2014-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多