我有一个XML如下所示:
<object>
<codes>
<cd1>A</cd1>
<cd2>B</cd2>
<cd3>C</cd3>
</codes>
<codes>
<cd1>A</cd1>
<cd2>D</cd2>
<cd3></cd3>
</codes>
<codes>
<cd1>E</cd1>
<cd2>D</cd2>
<cd3></cd3>
</codes>
</object>到目前为止,我的XPath发展过程如下:
//cd1|//cd2|//cd3:获取所有cd1、cd2和cd3元素(//cd1|//cd2|//cd3)[text()[1]]:从上面的列表中过滤所有具有非空文本值的元素,并返回以下元素。
<cd1>A</cd1> <cd2>B</cd2> <cd3>C</cd3> <cd1>A</cd1> <cd2>D</cd2> <cd1>E</cd1> <cd2>D</cd2>(//cd1|//cd2|//cd3)[text()[1]][(preceding::cd1)|(preceding::cd2)|(preceding::cd3)]。我希望实现的是检查该值是否在上面的cd1、cd2或cd3中的前面。但是,在<cd2>D</cd2>重复的情况下,这会返回到下面。
<cd2>B</cd2> <cd3>C</cd3> <cd1>A</cd1> <cd2>D</cd2> <cd1>E</cd1> <cd2>D</cd2>如何编写xpath来解决上述(3)问题?
请注意,我必须使用XPath1.0,因此distinct value函数不是一个选项。另外,我需要获得匹配的节点列表,而不是xpath中的文本值,因为我必须使用AXIOM对这些节点进行更多的处理。
Update :我正在使用这个xpath获取匹配的元素,然后使用AXIOM进行处理。因此,我需要编写一个xpath表达式来一次获得匹配的元素(我不能用AXIOM或使用XSLT编写自定义流)。另外,cd*不能使用,因为实名不匹配。我在这里用了一个样品。
发布于 2015-09-01 23:01:03
试试这个:
//cd1[not(text() = preceding::cd1/text())][normalize-space()]|
//cd2[not(text() = preceding::cd2/text())][normalize-space()]|
//cd3[not(text() = preceding::cd3/text())][normalize-space()]发布于 2015-09-01 21:37:46
我发现的一种方法是使用以下模板:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:for-each select="//*[starts-with(node-name(.), 'cd')]">
<xsl:variable name="content"><xsl:value-of select="text()"/></xsl:variable>
<xsl:if test="count(preceding::*[starts-with(node-name(.), 'cd') and text() = $content]) = 0 and text()">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>这将获取所有cd*元素,并获取每个cd*元素的内容,用于计算具有相同内容的前面有多少个--如果为0,则使用它。
据我所知,这是在xslt-1中(使用变量)的唯一方法。这是因为您不能返回xpath中的引用,除非变量中有值(并且需要将"current“(外部)文本与"current”(xpath中的节点)文本进行比较)。
希望这能有所帮助。
发布于 2015-09-01 22:20:22
这实际上是非常简单的Muenchian分组,只有三个键:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" method="xml" />
<xsl:key name="cd1" match="//cd1" use="text()" />
<xsl:key name="cd2" match="//cd2" use="text()" />
<xsl:key name="cd3" match="//cd3" use="text()" />
<xsl:template match="/">
<xsl:for-each select="/object/codes/cd1[./text() != '' and count(. | key('cd1', .)[1]) = 1]">
<xsl:copy-of select="." />
</xsl:for-each>
<xsl:for-each select="/object/codes/cd2[./text() != '' and count(. | key('cd2', .)[1]) = 1]">
<xsl:copy-of select="." />
</xsl:for-each>
<xsl:for-each select="/object/codes/cd3[./text() != '' and count(. | key('cd3', .)[1]) = 1]">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>输出:
<?xml version="1.0" encoding="UTF-8"?>
<cd1>A</cd1>
<cd1>E</cd1>
<cd2>B</cd2>
<cd2>D</cd2>
<cd3>C</cd3>或者,如果您想对它们进行分组,而不管节点名称(即,如果cd1和cd2都将A作为文本值),那么就不那么简单了。
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" method="xml" />
<xsl:key name="cd" match="//cd1 | //cd2 | //cd3" use="text()" />
<xsl:template match="/">
<xsl:for-each select="/object/codes/cd1[./text() != '' and count(. | key('cd', .)[1]) = 1] | /object/codes/cd2[./text() != '' and count(. | key('cd', .)[1]) = 1] | /object/codes/cd3[./text() != '' and count(. | key('cd', .)[1]) = 1]">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>这将提供与上面相同的输出(但按当前模板输出的方式排序),但将消除共享相同文本的cd1、cd2或cd3之间的重复(只使用第一个文本)。
还请注意,我忽略了可能不需要的空节点(并且可以通过从选择器中删除./text() != ''轻松地修复),但是,如果需要的话,必须使用不同的方法来消除重复的空节点(可能只是一系列测试空节点的模板或xsl:if,如果在这种情况下存在,则输出一个单独的模板)。
https://stackoverflow.com/questions/32340762
复制相似问题