2

首先,让我说我喜欢阅读有关合并多个 XML 文件的许多技巧。我也很喜欢实施其中的很多。但是我还没有达到我的目标。

我不想简单地合并 XML 文件,以便在生成的 XML 文件中一个接一个地重复。我有需要合并重复元素的组:

<SAN>
  <EQLHosts>
    <WindowsHosts>
      <WindowsHost>
        more data and structures down here...
      </WindowsHost>
    </WindowsHosts>
    <LinuxHosts>
      <LinuxHost>
        ...and here...
      </LinuxHost>
    </LinuxHosts>
  </EQLHosts>
</SAN>

每个单独的 XML 文件都可能有 Windows 和/或 Linux 主机。因此,如果 XML 文件 1 具有 Windows 主机ABC的数据,而 XML 文件 2 具有 Windows 主机DEF的数据,则生成的 XML 应如下所示:

<SAN>
  <EQLHosts>
    <WindowsHosts>
      <WindowsHost>
        <Name>A</Name>
      </WindowsHost>
      <WindowsHost>
        <Name>B</Name>
      </WindowsHost>
      <WindowsHost>
        <Name>C</Name>
      </WindowsHost>
      <WindowsHost>
        <Name>D</Name>
      </WindowsHost>
      <WindowsHost>
        <Name>E</Name>
      </WindowsHost>
      <WindowsHost>
        <Name>F</Name>
      </WindowsHost>
    </WindowsHosts>
    <LinuxHosts>
      <LinuxHost/>
    </LinuxHosts>
  </EQLHosts>
</SAN>

我已经使用了这个 XSLT 来让它工作:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:variable name="file1" select="document('CorralData1.xml')"/>
  <xsl:variable name="file2" select="document('CorralData2.xml')"/>
  <xsl:variable name="file3" select="document('CorralData3.xml')"/>

  <xsl:template match="/">
    <SAN>
      <xsl:copy-of select="/SAN/*"/>
      <xsl:copy-of select="$file1/SAN/*"/>
      <xsl:copy-of select="$file2/SAN/*"/>
      <xsl:copy-of select="$file3/SAN/*"/>
    </SAN>
  </xsl:template>

</xsl:stylesheet>

此文件生成一个组合 XSLT,其中包含正确包含在树下的所有数据,但具有多个 WindowsHosts 实例。不想那样。

有没有办法用最少的语法告诉 XSLT 如何做到这一点,或者我是否需要在 XSLT 文件中专门添加每个元素和子元素?


我应该检查一下。但是我继续使用了 collection() 并得到了一个使用 Saxon HE XSLT 处理器完美工作的解决方案。

但是我在 InfoPath 环境中运行,并且只有一个 XSLT 1.0 处理器。是否有人建议在 XSLT 1.0 环境中替换 collection() 命令?我可以以某种方式重新使用 document() 吗?


所以我现在有这个文件......

<?xml version="1.0" encoding="windows-1252"?>

<files>
    <file name="CorralData1.xml"/>
    <file name="CorralData2.xml"/>
</files>

...我与包含...的样式表一起使用

<xsl:variable name="windowsHosts" select="/SAN/WindowsHosts/WindowsHost"/>
<xsl:variable name="vmwareHosts" select="/SAN/VMwareHosts/VMwareHost"/>
<xsl:variable name="linuxHosts" select="/SAN/LinuxHosts/LinuxHost"/>

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

<xsl:template match="/">
    <xsl:for-each select="/files/file">
        <xsl:apply-templates select="document(@name)/SAN"/>
    </xsl:for-each>
    <SAN>
        <EQLHosts>
            <WindowsHosts>
                <xsl:for-each select="$windowsHosts">
                    <xsl:copy-of select="."/>
                </xsl:for-each>
            </WindowsHosts>
            <VMwareHosts>
                <xsl:for-each select="$vmwareHosts">
                    <xsl:copy-of select="."/>
                </xsl:for-each>                 
            </VMwareHosts>
            <LinuxHosts>
                <xsl:for-each select="$linuxHosts">
                    <xsl:copy-of select="."/>
                </xsl:for-each>                 
            </LinuxHosts>
        </EQLHosts>
    </SAN>
</xsl:template>

...但这让我获得了多个 /SAN 根。我已经很接近了,但有些事情还是有点不对劲。

4

2 回答 2

3

我要做的是distinct-values()用来获取每个唯一的主机名。你也可以使用collection()它来使它更容易一些。(用法可能因实现而异。我使用的是 Saxon 9.4。)

例子...

目录“input_dir”中的输入文件...

CorralData1.xml

<SAN>
    <EQLHosts>
        <WindowsHosts>
            <WindowsHost>
                <Name>Windows-A</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-B</Name>
            </WindowsHost>
        </WindowsHosts>
        <LinuxHosts>
            <LinuxHost>
                <Name>Linux-A</Name>
            </LinuxHost>
            <LinuxHost>
                <Name>Linux-B</Name>
            </LinuxHost>
        </LinuxHosts>
    </EQLHosts>
</SAN>

CorralData2.xml(重复 Windows-A 和 Windows-B)

<SAN>
    <EQLHosts>
        <WindowsHosts>
            <WindowsHost>
                <Name>Windows-C</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-D</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-A</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-B</Name>
            </WindowsHost>
        </WindowsHosts>
        <LinuxHosts>
            <LinuxHost>
                <Name>Linux-C</Name>
            </LinuxHost>
            <LinuxHost>
                <Name>Linux-D</Name>
            </LinuxHost>
        </LinuxHosts>
    </EQLHosts>
</SAN>

CorralData3.xml(重复 Windows-A 和 Windows-B)

<SAN>
    <EQLHosts>
        <WindowsHosts>
            <WindowsHost>
                <Name>Windows-E</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-F</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-A</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-B</Name>
            </WindowsHost>          
        </WindowsHosts>
        <LinuxHosts>
            <LinuxHost>
                <Name>Linux-E</Name>
            </LinuxHost>
            <LinuxHost>
                <Name>Linux-F</Name>
            </LinuxHost>
        </LinuxHosts>
    </EQLHosts>
</SAN>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:variable name="collection">
        <xsl:copy-of select="collection('input_dir?strip-space=yes;select=*.xml')/*"/>
    </xsl:variable>
    <xsl:variable name="windowsHosts" select="distinct-values($collection/SAN/EQLHosts/WindowsHosts/WindowsHost/Name)"/>
    <xsl:variable name="linuxHosts" select="distinct-values($collection/SAN/EQLHosts/LinuxHosts/LinuxHost/Name)"/>

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

    <xsl:template match="/">
        <SAN>
            <EQLHosts>
                <WindowsHosts>
                    <xsl:for-each select="$windowsHosts">
                        <xsl:apply-templates select="($collection/SAN/EQLHosts/WindowsHosts/WindowsHost[Name=current()])[1]"/>
                    </xsl:for-each>
                </WindowsHosts>
                <LinuxHosts>
                    <xsl:for-each select="$linuxHosts">
                        <xsl:apply-templates select="($collection/SAN/EQLHosts/LinuxHosts/LinuxHost[Name=current()])[1]"/>
                    </xsl:for-each>                 
                </LinuxHosts>
            </EQLHosts>
        </SAN>
    </xsl:template>

</xsl:stylesheet>

输出

<SAN>
    <EQLHosts>
        <WindowsHosts>
            <WindowsHost>
                <Name>Windows-A</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-B</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-C</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-D</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-E</Name>
            </WindowsHost>
            <WindowsHost>
                <Name>Windows-F</Name>
            </WindowsHost>
        </WindowsHosts>
        <LinuxHosts>
            <LinuxHost>
                <Name>Linux-A</Name>
            </LinuxHost>
            <LinuxHost>
                <Name>Linux-B</Name>
            </LinuxHost>
            <LinuxHost>
                <Name>Linux-C</Name>
            </LinuxHost>
            <LinuxHost>
                <Name>Linux-D</Name>
            </LinuxHost>
            <LinuxHost>
                <Name>Linux-E</Name>
            </LinuxHost>
            <LinuxHost>
                <Name>Linux-F</Name>
            </LinuxHost>
        </LinuxHosts>
    </EQLHosts>
</SAN>
于 2013-06-01T01:34:24.820 回答
1

我为此操作使用了两个 XSLT 文件。第一个只是附加所有文件:

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

<xsl:template match="/">
    <SAN>
        <xsl:apply-templates select="document('MainDataSource.xml')/SAN/*"/>
        <xsl:apply-templates select="document('CorralData1.xml')/SAN/*"/>
        <xsl:apply-templates select="document('CorralData2.xml')/SAN/*"/>
    </SAN>
</xsl:template>

第二个按组合并数据:

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

<xsl:template match="*">
    <SAN>
        <ClientProfile>
        </ClientProfile>
        <STACKMEMBERS>
            <xsl:for-each select="/SAN/STACKMEMBERS/STACKMEMBER">
                <xsl:copy-of select="."/>
            </xsl:for-each>
        </STACKMEMBERS>
        <Force10StackMembers>
            <xsl:for-each select="/SAN/Force10StackMembers/Force10StackMember">
                <xsl:copy-of select="."/>
            </xsl:for-each>
        </Force10StackMembers>
    </SAN>
</xsl:template>
于 2013-06-13T17:30:07.553 回答