使用XSLT基于属性值合并2个XML文件(包括节点属性)

时间:2017-02-13 11:56:57

标签: xml xslt merge

注意:这不是重复的 Merge 2 XML files based on attribute values using XSLT? 但是它的扩展。给出以下输入文件

file1.xml

<config>
 <state version="10">
  <root value="100" group="5">
     <leaf number = "2"/>
  </root>
  <root value="101" group="6" overrideAttr="oldval">
     <leaf number = "3"/>
  </root>
 </state>
</config>

file2.xml

<config>
 <state version="10">
  <root value="100" group="5">
     <leaf number = "6"/>
  </root>
  <root value="101" group="6" overrideAttr="newval" addtionalAttr="hello">
     <leaf number = "4"/>
  </root>
 </state>
</config>

我想要这个output.xml

<config>
 <state version="10">
  <root value="100" group="5">
     <leaf number = "2"/>
     <leaf number = "6"/>
  </root>
  <root value="101" group="6" overrideAttr="newval" addtionalAttr="hello">
     <leaf number = "3"/>
     <leaf number = "4"/>
  </root>
 </state>
</config>

所需的扩展程序

  • 属性(例如overrideAttr)在&#34;同一节点&#34; (例如,元素根值为&#34; 101&#34;和group =&#34; 6&#34;)应该被覆盖
  • 应添加新属性(例如addtionalAttr)

这可以通过xsl实现吗?

2 个答案:

答案 0 :(得分:1)

在您链接的答案中,有xsl:apply-templates复制第二个文件中的子元素。

  <xsl:apply-templates
    select="document('file2.xml')
          /config/state[@version = current()/../@version]
                 /root[@value = current()/@value and
                       @group = current()/@group]/*" />

您需要做的就是添加一个类似的行来复制属性

  <xsl:apply-templates
    select="document('file2.xml')
          /config/state[@version = current()/../@version]
                 /root[@value = current()/@value and
                       @group = current()/@group]/@*" />

虽然这需要在复制任何现有子节点之前完成(因为必须在子节点之前添加属性)。

此外,您可能希望使用变量来避免重复xpath表达式。

试试这个XSLT ......

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" omit-xml-declaration="yes"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="root">
        <xsl:variable name="file2root" select="document('file2.xml')
              /config/state[@version = current()/../@version]
                     /root[@value = current()/@value and
                           @group = current()/@group]" />
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates select="$file2root/@*" />
            <xsl:apply-templates select="node()" />
      <xsl:apply-templates select="$file2root/*" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

请注意,这利用了以下事实:“向元素添加属性会将该元素的任何现有属性替换为具有相同扩展名称的”。 (见https://www.w3.org/TR/xslt#creating-attributes

答案 1 :(得分:0)

如果您想扩展该解决方案,那么您可以按照以下方式执行此操作,将root元素(http://xsltransform.net/gWEamLR/1)的模板更改为

      <xsl:template match="root">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates select="$doc2
              /config/state[@version = current()/../@version]
                     /root[@value = current()/@value and
                           @group = current()/@group]/@*" />
      <xsl:apply-templates select="node()"/>
      <xsl:apply-templates select="
        $doc2
              /config/state[@version = current()/../@version]
                     /root[@value = current()/@value and
                           @group = current()/@group]/*" />
    </xsl:copy>
  </xsl:template>

并确保定义<xsl:param name="doc2" select="document('file2.xml')"/>

可能有更好的方法使用键或分组进行合并,当然在XSLT 3.0中我们现在有xsl:merge

<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:strip-space elements="*"/>

    <xsl:template match="/" name="main">
        <config>
            <xsl:merge>
                <xsl:merge-source select="doc('file1.xml')/config/state">
                    <xsl:merge-key select="@version"/>
                </xsl:merge-source>
                <xsl:merge-source select="doc('file2.xml')/config/state">
                    <xsl:merge-key select="@version"/>
                </xsl:merge-source>
                <xsl:merge-action>
                    <xsl:copy>
                        <xsl:copy-of select="@*"/>
                        <xsl:merge>
                            <xsl:merge-source select="current-merge-group()[1]/root">
                                <xsl:merge-key select="@value"/>
                                <xsl:merge-key select="@group"/>
                            </xsl:merge-source>
                            <xsl:merge-source select="current-merge-group()[2]/root">
                                <xsl:merge-key select="@value"/>
                                <xsl:merge-key select="@group"/>
                            </xsl:merge-source>
                            <xsl:merge-action>
                                <xsl:copy>
                                    <xsl:copy-of select="current-merge-group()[2]/@*"/>
                                    <xsl:copy-of select="current-merge-group()/node()"/>
                                </xsl:copy>
                            </xsl:merge-action>
                        </xsl:merge>
                    </xsl:copy>
                </xsl:merge-action>
            </xsl:merge>
        </config>
    </xsl:template>

</xsl:stylesheet>