我有几个XML文件,如下所示:
文件 : 1.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val1</info1>
<info2>val2</info2>
</info>
<info>
<info1>val3</info1>
<info2>val4</info2>
</info>
</config>
文件 : 2.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val5</info1>
<info2>val6</info2>
</info>
<info>
<info1>val7</info1>
<info2>val8</info2>
</info>
</config>
文件: 3.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val9</info1>
<info2>val10</info2>
</info>
<info>
<info1>val11</info1>
<info2>val12</info2>
</info>
</config>
使用 XSLT2.0(撒克逊),我想合并它们并添加到每个节点:
<info3>XXX</info3>
还有
<file>filename.xml</file>
文件名.xml 是从中复制信息的文件。
输出应如下所示:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val1</info1>
<info2>val2</info2>
<info3>XXX</info3>
<file>1.xml</file>
</info>
<info>
<info1>val3</info1>
<info2>val4</info2>
<info3>XXX</info3>
<file>1.xml</file>
</info>
<info>
<info1>val5</info1>
<info2>val6</info2>
<info3>XXX</info3>
<file>2.xml</file>
</info>
<info>
<info1>val7</info1>
<info2>val8</info2>
<info3>XXX</info3>
<file>2.xml</file>
</info>
<info>
<info1>val9</info1>
<info2>val10</info2>
<info3>XXX</info3>
<file>3.xml</file>
</info>
<info>
<info1>val11</info1>
<info2>val12</info2>
<info3>XXX</info3>
<file>3.xml</file>
</info>
</config>
到目前为止,我已经能够通过创建一个列出我要合并的文件的 XML 文件来合并文件(merge.xml):
<mergeData newRoot="config">
<filelist>
<fileItem>1.xml</fileItem>
<fileItem>2.xml</fileItem>
<fileItem>3.xml</fileItem>
</filelist>
</mergeData>
使用以下 XSL (merge.xsl):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="#all">
<xsl:param name="new">
<info>XXX</info>
</xsl:param>
<xsl:template match="/">
<xsl:element name="{mergeData/@newRoot}">
<xsl:apply-templates select="mergeData/fileList/fileItem"/>
</xsl:element>
</xsl:template>
<xsl:template match="fileItem">
<xsl:apply-templates select="document(translate(., '', '/'))/config/*"/>
</xsl:template>
<xsl:template match="config/*">
<xsl:copy>
<xsl:copy-of select="node()"/>
<xsl:copy-of select="$new"/>
</xsl:copy>
<file><xsl:value-of select="tokenize(document-uri(.), '/')[last()]"/></file>
</xsl:template>
我应该如何修改 XSL 以同时将文件名放入每个信息中。
实际上,您唯一要做的就是将file
移动到xsl:copy
内。
示例(还有其他几个小模组):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="#all">
<xsl:output indent="yes"/>
<xsl:param name="new">
<info3>XXX</info3>
</xsl:param>
<xsl:template match="/">
<xsl:element name="{mergeData/@newRoot}">
<xsl:apply-templates select="mergeData/filelist/fileItem"/>
</xsl:element>
</xsl:template>
<xsl:template match="fileItem">
<xsl:apply-templates select="document(translate(., '', '/'))/config/*"/>
</xsl:template>
<xsl:template match="config/*">
<xsl:copy>
<xsl:copy-of select="node(),$new"/>
<file><xsl:value-of select="tokenize(document-uri(/), '/')[last()]"/></file>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
您也可以使用 collection()
而不是创建单独的 mergeData.xml
文件来执行此操作:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="newRoot" select="'config'"/>
<xsl:param name="new">
<info3>XXX</info3>
</xsl:param>
<xsl:template match="/">
<xsl:element name="{$newRoot}">
<xsl:apply-templates select="collection('file:///C:/some/path?select=[0-9]*.xml')/*/info"/>
</xsl:element>
</xsl:template>
<xsl:template match="info">
<xsl:copy>
<xsl:copy-of select="@*|node(),$new"/>
<file><xsl:value-of select="tokenize(document-uri(/),'/')[last()]"/></file>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
由于您使用的是 Saxon,另一种选择是将saxon:discard-document()
与mergeData.xml
输入一起使用。如果您在 mergeData.xml
中列出了大量文件,这有助于减少内存消耗。(它确实需要 Saxon PE 或 EE 或允许扩展功能的旧版本的 Saxon。
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="new">
<info3>XXX</info3>
</xsl:param>
<xsl:template match="/mergeData">
<xsl:element name="{@newRoot}">
<xsl:apply-templates select="filelist/fileItem"/>
</xsl:element>
</xsl:template>
<xsl:template match="fileItem">
<xsl:apply-templates select="document(.)/saxon:discard-document(.)/*/*" xmlns:saxon="http://saxon.sf.net/"/>
</xsl:template>
<xsl:template match="info">
<xsl:copy>
<xsl:copy-of select="@*|node(),$new"/>
<file><xsl:value-of select="tokenize(document-uri(/),'/')[last()]"/></file>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
以下 XSLT 将生成所需的结果:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:param name="new">
<info>XXX</info>
</xsl:param>
<xsl:template match="/mergeData">
<config>
<xsl:for-each select="filelist/fileItem">
<xsl:variable name="filename" select="text()"/>
<xsl:for-each select="document($filename)/config/info">
<info>
<xsl:copy-of select="./*"/>
<xsl:element name="info{count(*)+1}">
<xsl:value-of select="$new"/>
</xsl:element>
<file><xsl:value-of select="$filename"/></file>
</info>
</xsl:for-each>
</xsl:for-each>
</config>
</xsl:template>
</xsl:stylesheet>
笔记:
- 这已经适用于 XSLT 1.0,因此我更改了 XSLT 声明。
- 由于这种方法是自上而下的预定义子结构,因此它不再使用输入文件的属性
newRoot
。 - 答案不会提取输入文件的基本名称,而是使用合并配置中提供的完整路径。您可能希望还原此简化。当然,使用
tokenize
会将其推回 XSLT 2.0 或扩展函数。