简体   繁体   English

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

[英]Merge 2 XML files (including node attributes) based on attribute values using XSLT

Note: this is NOT a duplicate of Merge 2 XML files based on attribute values using XSLT?注意:这不是使用 XSLT 基于属性值Merge 2 XML 文件的副本吗? but an extension to it.但它的扩展。 Given the following input files鉴于以下输入文件

file1.xml文件1.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文件2.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>

I'd like to have this output.xml我想要这个 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>

Desired extensions are所需的扩展是

  • attributes (eg overrideAttr) on the "same node" (eg element root with value="101" and group="6") should be overwritten应该覆盖“相同节点”上的属性(例如 overrideAttr)(例如 value="101" 和 group="6" 的元素根)
  • new attributes (eg addtionalAttr) should be added应添加新属性(例如 addtionalAttr)

Can this be achieved by xsl?这可以通过xsl实现吗?

In the answer that you have linked to, there is an xsl:apply-templates that copies across the child elements from the second file.在您链接到的答案中,有一个xsl:apply-templates复制第二个文件中的子元素。

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

All you need to do is add a similar line to copy across attributes您需要做的就是添加一个类似的行来跨属性复制

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

Although this would need to done before the copying of any existing child nodes (as attributes must be added before child nodes).尽管这需要在复制任何现有子节点之前完成(因为必须在子节点之前添加属性)。

Additionally, you might want to use a variable to avoid repeating the xpath expression.此外,您可能希望使用变量来避免重复 xpath 表达式。

Try this XSLT...试试这个 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>

Note that this takes advantage of the fact that " Adding an attribute to an element replaces any existing attribute of that element with the same expanded-name ".请注意,这利用了“向元素添加属性会用相同的扩展名替换该元素的任何现有属性”这一事实。 (See https://www.w3.org/TR/xslt#creating-attributes ) (见https://www.w3.org/TR/xslt#creating-attributes

If you want to extend that solution then you can do it as follows, change that template for the root elements ( http://xsltransform.net/gWEamLR/1 ) to如果您想扩展该解决方案,那么您可以按如下方式进行,将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>

and make sure you define <xsl:param name="doc2" select="document('file2.xml')"/> .并确保您定义了<xsl:param name="doc2" select="document('file2.xml')"/>

There might be better ways using keys or grouping doing that merging, and of course in XSLT 3.0 we now have xsl:merge使用键或分组进行合并可能有更好的方法,当然在 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM