select 唯一节点的 XPath 表达式
XPath expression to select unique nodes
我正在做一个项目,我必须将一些 XML 输入转换为一些 XML 输出,为此我使用 XSLT 版本 1。
我正在处理的输入 XML 文件是 巨大的 大约 10k+ 行,但我花了一个小时的大部分时间将其简化为以下代码片段,它捕获了问题。
这是输入XML
<QueryInput >
<Subject>
<Content>
<MunicipalityCode>0217</MunicipalityCode>
</Content>
</Subject>
<QueryResultStep>
<Multistep>
<IterationResponse>
<QueryResult>
<Kommune>0217</Kommune>
</QueryResult>
</IterationResponse>
<IterationResponse>
<QueryResult>
<Kommune>0217</Kommune>
</QueryResult>
</IterationResponse>
<IterationResponse>
<QueryResult>
<Kommune>0223</Kommune>
</QueryResult>
</IterationResponse>
<IterationResponse>
<QueryResult>
<Kommune>0223</Kommune>
</QueryResult>
</IterationResponse>
</Multistep>
</QueryResultStep>
</QueryInput>
输出 XML 应包含每个 "Kommune" 一次,删除重复项。为此,我编写了以下 XSLT 代码。
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="xsl xsi xsd">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<QueryResult>
<xsl:variable name="something">
<KommuneCollection>
<xsl:for-each select="QueryInput/QueryResultStep/Multistep/IterationResponse/QueryResult/Kommune[not(.=preceding::*)]">
<NewKommune>
<xsl:value-of select="."/>
</NewKommune>
</xsl:for-each>
</KommuneCollection>
</xsl:variable>
<xsl:copy-of select="$something"/>
</QueryResult>
</xsl:template>
</xsl:transform>
产生以下(几乎正确的)输出:
<KommuneCollection>
<NewKommune>0223</NewKommune>
</KommuneCollection>
但应该生产
<KommuneCollection>
<NewKommune>0217</NewKommune>
<NewKommune>0223</NewKommune>
</KommuneCollection>
如果我删除输入 XML 中的 <MunicipalityCode>0217</MunicipalityCode>
,它会突然起作用 - 但我 真的 不明白为什么。不是为什么会这样,我也不知道如何解决这个问题。非常感谢任何帮助!
编辑:通过将输入 XML 复制到 Notepad++,安装 XPathenizer 工具,显示 window 并输入此 XPath 表达式 QueryInput/QueryResultStep/Multistep/IterationResponse/QueryResult/Kommune[not(.=preceding::*)]
,并执行表达方式。然后可以在右侧看到结果。我怀疑问题出在 XSLT 中 for-each
标记中使用的 XPath 表达式。
正如michael.hor257k所说,Muenchian 的分组将对您有所帮助(处理大文件)。但是,以下将是您当前尝试的正确版本:
<xsl:transform version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exclude-result-prefixes="xsl xsi xsd">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<QueryResult>
<KommuneCollection>
<xsl:for-each select="QueryInput/QueryResultStep/Multistep/IterationResponse/QueryResult/Kommune[not(. = preceding::QueryResult/Kommune )]">
<NewKommune>
<xsl:value-of select="."/>
</NewKommune>
</xsl:for-each>
</KommuneCollection>
</QueryResult>
</xsl:template>
</xsl:transform>
注意:这种方式效率较低。使用 Muenchian 的分组,您会感受到不同。
您的谓词本来可以工作,但未能包含“217”,因为 /QueryInput/Subject/Content/MunicipalityCode
恰好具有值“217”。
如果您调整谓词过滤器以匹配前面的 Kommune
元素而不是任何前面的元素,那么它将产生所需的结果:
[not(.=preceding::Kommune)]
但是,它不是很有效。如果您的文件很大,那么使用 xsl:key()
和 meunchian method 会更高效。
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform version="1.0"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="xsl xsi xsd">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="Kommune" match="Kommune" use="."/>
<xsl:template match="/">
<QueryResult>
<xsl:variable name="something">
<KommuneCollection>
<xsl:for-each
select="QueryInput/QueryResultStep/Multistep/
IterationResponse/QueryResult/
Kommune[generate-id(.) =
generate-id(key('Kommune',.)[1])]">
<NewKommune>
<xsl:value-of select="."/>
</NewKommune>
</xsl:for-each>
</KommuneCollection>
</xsl:variable>
<xsl:copy-of select="$something"/>
</QueryResult>
</xsl:template>
</xsl:transform>
我正在做一个项目,我必须将一些 XML 输入转换为一些 XML 输出,为此我使用 XSLT 版本 1。
我正在处理的输入 XML 文件是 巨大的 大约 10k+ 行,但我花了一个小时的大部分时间将其简化为以下代码片段,它捕获了问题。
这是输入XML
<QueryInput >
<Subject>
<Content>
<MunicipalityCode>0217</MunicipalityCode>
</Content>
</Subject>
<QueryResultStep>
<Multistep>
<IterationResponse>
<QueryResult>
<Kommune>0217</Kommune>
</QueryResult>
</IterationResponse>
<IterationResponse>
<QueryResult>
<Kommune>0217</Kommune>
</QueryResult>
</IterationResponse>
<IterationResponse>
<QueryResult>
<Kommune>0223</Kommune>
</QueryResult>
</IterationResponse>
<IterationResponse>
<QueryResult>
<Kommune>0223</Kommune>
</QueryResult>
</IterationResponse>
</Multistep>
</QueryResultStep>
</QueryInput>
输出 XML 应包含每个 "Kommune" 一次,删除重复项。为此,我编写了以下 XSLT 代码。
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="xsl xsi xsd">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<QueryResult>
<xsl:variable name="something">
<KommuneCollection>
<xsl:for-each select="QueryInput/QueryResultStep/Multistep/IterationResponse/QueryResult/Kommune[not(.=preceding::*)]">
<NewKommune>
<xsl:value-of select="."/>
</NewKommune>
</xsl:for-each>
</KommuneCollection>
</xsl:variable>
<xsl:copy-of select="$something"/>
</QueryResult>
</xsl:template>
</xsl:transform>
产生以下(几乎正确的)输出:
<KommuneCollection>
<NewKommune>0223</NewKommune>
</KommuneCollection>
但应该生产
<KommuneCollection>
<NewKommune>0217</NewKommune>
<NewKommune>0223</NewKommune>
</KommuneCollection>
如果我删除输入 XML 中的 <MunicipalityCode>0217</MunicipalityCode>
,它会突然起作用 - 但我 真的 不明白为什么。不是为什么会这样,我也不知道如何解决这个问题。非常感谢任何帮助!
编辑:通过将输入 XML 复制到 Notepad++,安装 XPathenizer 工具,显示 window 并输入此 XPath 表达式 QueryInput/QueryResultStep/Multistep/IterationResponse/QueryResult/Kommune[not(.=preceding::*)]
,并执行表达方式。然后可以在右侧看到结果。我怀疑问题出在 XSLT 中 for-each
标记中使用的 XPath 表达式。
正如michael.hor257k所说,Muenchian 的分组将对您有所帮助(处理大文件)。但是,以下将是您当前尝试的正确版本:
<xsl:transform version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exclude-result-prefixes="xsl xsi xsd">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<QueryResult>
<KommuneCollection>
<xsl:for-each select="QueryInput/QueryResultStep/Multistep/IterationResponse/QueryResult/Kommune[not(. = preceding::QueryResult/Kommune )]">
<NewKommune>
<xsl:value-of select="."/>
</NewKommune>
</xsl:for-each>
</KommuneCollection>
</QueryResult>
</xsl:template>
</xsl:transform>
注意:这种方式效率较低。使用 Muenchian 的分组,您会感受到不同。
您的谓词本来可以工作,但未能包含“217”,因为 /QueryInput/Subject/Content/MunicipalityCode
恰好具有值“217”。
如果您调整谓词过滤器以匹配前面的 Kommune
元素而不是任何前面的元素,那么它将产生所需的结果:
[not(.=preceding::Kommune)]
但是,它不是很有效。如果您的文件很大,那么使用 xsl:key()
和 meunchian method 会更高效。
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform version="1.0"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="xsl xsi xsd">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="Kommune" match="Kommune" use="."/>
<xsl:template match="/">
<QueryResult>
<xsl:variable name="something">
<KommuneCollection>
<xsl:for-each
select="QueryInput/QueryResultStep/Multistep/
IterationResponse/QueryResult/
Kommune[generate-id(.) =
generate-id(key('Kommune',.)[1])]">
<NewKommune>
<xsl:value-of select="."/>
</NewKommune>
</xsl:for-each>
</KommuneCollection>
</xsl:variable>
<xsl:copy-of select="$something"/>
</QueryResult>
</xsl:template>
</xsl:transform>