用于选择唯一节点的XPath表达式

时间:2015-01-08 23:59:58

标签: xml xslt xpath xslt-1.0

我正在开发一个项目,我必须将一些XML输入转换为某些XML输出,为此我使用的是XSLT版本1.

我正在处理的输入XML文件是 large ,例如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应该包含每个&#34; Kommune&#34;一次,删除重复。为此,我制作了以下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工具,显示窗口并输入此XPath表达式QueryInput/QueryResultStep/Multistep/IterationResponse/QueryResult/Kommune[not(.=preceding::*)]并执行表达式,可以轻松复制该问题。然后可以在右侧看到结果。我怀疑XSLT中for-each标记中使用的XPath表达式存在问题。

2 个答案:

答案 0 :(得分:2)

正如 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的分组时,你会感受到不同。

答案 1 :(得分:1)

你的谓词本来有用,但未能包含&#34; 217&#34;因为/QueryInput/Subject/Content/MunicipalityCode碰巧有值&#34; 217&#34;。

如果您调整谓词过滤器以匹配前面的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>