首先,请允许我说我很喜欢阅读有关合并多个XML文件的几十个提示。我也很喜欢实施其中的很多。但我还没有实现我的目标。
我不想简单地合并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主机 A , B 和 C 的数据,则XML文件2包含Windows主机 D的数据, E 和 F ,生成的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根源。我很接近,但有些事情仍然有点不对。
答案 0 :(得分:2)
我要做的是使用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>
答案 1 :(得分:0)
我使用了两个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>