2009-11-30 83 views
3

使用此源文档:问题使用祖先轴在XPath谓词

<?xml version="1.0" encoding="UTF-8"?> 
<Root> 
    <Element1 id="UniqueId1"> 
    <SubElement1/> 
    <SubElement2> 
     <LeafElement1/> 
     <LeafElement1/> 
    </SubElement2> 
    </Element1> 
    <Element2 id="UniqueId2" AttributeToCheck="true"> 
    <SubElement1> 
     <LeafElement1/> 
     <LeafElement1/> 
    </SubElement1> 
    </Element2> 
</Root> 

欲属性富=“栏中的”添加到该两个元件:

  1. 具有与同级元素同名
  2. 有任何祖先属性AttributeToCheck

这应该是RESU lt:

<?xml version="1.0"?> 
<Root> 
    <Element1 id="UniqueId1"> 
    <SubElement1/> 
    <SubElement2> 
     <LeafElement1/> 
     <LeafElement1/> 
    </SubElement2> 
    </Element1> 
    <Element2 id="UniqueId2" AttributeToCheck="true"> 
    <SubElement1> 
     <LeafElement1 foo="bar"/> 
     <LeafElement1 foo="bar"/> 
    </SubElement1> 
    </Element2> 
</Root> 

这是我的表单到目前为止。它增加了匹配条件1的属性的元素,但无法说清条件2

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" version="1.0" indent="yes"/> 

    <xsl:template match="* | @*"> 
    <xsl:copy> 
     <xsl:apply-templates select="* | @*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*[count(../*[name(.) = name(current())]) > 1]"> 
    <xsl:copy> 
     <xsl:attribute name="foo">bar</xsl:attribute> 
     <xsl:apply-templates select="* | @*"/> 
    </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

不正确的输出:

<?xml version="1.0"?> 
<Root> 
    <Element1 id="UniqueId1"> 
    <SubElement1/> 
    <SubElement2> 
     <LeafElement1 foo="bar"/> (incorrect) 
     <LeafElement1 foo="bar"/> (incorrect) 
    </SubElement2> 
    </Element1> 
    <Element2 id="UniqueId2" AttributeToCheck="true"> 
    <SubElement1> 
     <LeafElement1 foo="bar"/> (correct) 
     <LeafElement1 foo="bar"/> (correct) 
    </SubElement1> 
    </Element2> 
</Root> 

由于第二模板已经正确地匹配具有相同名称的兄弟姐妹元素,应该很容易使用祖先XPath轴来排除没有AttributeToCheck祖先的元素。我在第二个模板中添加了另一个谓词。

<xsl:template match="*[ancestor::*[@AttributeToCheck]][count(../*[name(.) = name(current())]) > 1]"> 

当我应用此样式表时,输出文档与输入文档相同,表明第二个模板与任何元素都不匹配。我也尝试改变新的谓词来使用节点数。

<xsl:template match="*[count(ancestor::*[@AttributeToCheck]) > 0][count(../*[name(.) = name(current())]) > 1]"> 

这也行不通,输出文件与输入文件相同。这是令人惊讶的,因为当我使用这个祖先表达式来输出具有AttributeToCheck的节点名称时,它就可以工作。我做了这些改动sylesheet:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" version="1.0" indent="yes"/> 

    <xsl:template match="* | @*"> 
    <xsl:copy> 
     <xsl:apply-templates select="* | @*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*[count(../*[name(.) = name(current())]) > 1]"> 
    <xsl:copy> 
     <xsl:attribute name="foo">bar</xsl:attribute> 
     <xsl:attribute name="AncestorCount"> 
     <xsl:value-of select="count(ancestor::*[@AttributeToCheck])"/> 
     </xsl:attribute> 
     <xsl:attribute name="AncestorName"> 
     <xsl:value-of select="name(ancestor::*[@AttributeToCheck])"/> 
     </xsl:attribute> 
     <xsl:apply-templates select="* | @*"/> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

产生这样的输出:

<?xml version="1.0"?> 
<Root> 
    <Element1 id="UniqueId1"> 
    <SubElement1/> 
    <SubElement2> 
     <LeafElement1 foo="bar" AncestorCount="0" AncestorName=""/> 
     <LeafElement1 foo="bar" AncestorCount="0" AncestorName=""/> 
    </SubElement2> 
    </Element1> 
    <Element2 id="UniqueId2" AttributeToCheck="true"> 
    <SubElement1> 
     <LeafElement1 foo="bar" AncestorCount="1" AncestorName="Element2"/> 
     <LeafElement1 foo="bar" AncestorCount="1" AncestorName="Element2"/> 
    </SubElement1> 
    </Element2> 
</Root> 

我的问题是,为什么XPath的谓词*[ancestor::*[@AttributeToCheck]][count(../*[name(.) = name(current())]) > 1]不正确匹配匹配两个条件1和2的元素?我应该使用什么XPath表达式?

+1

您使用的是哪种XSL库? .NET XSL库无法在模式内处理'current()',并且(一旦我得到了补偿),* [ancestor :: * [@ AttributeToCheck]] [count(../* [name(。)= name(current())])> 1]'表达式按预期工作。我认为你的XSL库已经搞乱了。 – Welbog 2009-11-30 15:29:08

+0

它也适用于W3Cchool的try it编辑器:http://www.w3schools.com/Xsl/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog – Welbog 2009-11-30 15:39:10

+0

我使用的是XmlStarlet 1.0.1,我认为它是当前的版本。 XmlStarlet使用libxml2-2.7.6和libxslt-1.1.26。 – Chris 2009-11-30 16:25:46

回答

1

我认为这是XSL处理器上的问题,而不是您的XSLT。 (除count(ancestor::*[@AttributeToCheck]) > 0应如ancestor::*[@AttributeToCheck='true'],因为你可能会注意到)

我尝试以下,似乎在XmlStarlet正常工作:

<xsl:template match="*[ancestor::*[@AttributeToCheck='true'] and count(../*[name(.)=name(current())]) > 1]"> 

在我的§2.1和解释§3.4的XPath 1.0规范,在这种情况下,您的表达式和上面的表达式具有相同的效果(至少)。

另一种解决方案是使用带有模式的模板。 如果@AttributeToCheck可以嵌套,这会很有用。

+0

这看起来是由于XmlStarlet中的错误造成的。哈贝的XPath表达式与我的功能相当,但他的作品和我的作品都没有。 – Chris 2009-12-02 05:35:24

3

我会只用一个模板(模式匹配是昂贵的):

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" version="1.0" indent="yes"/> 

    <xsl:template match="* | @*"> 
    <xsl:copy> 
     <xsl:if test="count(../*[name(current()) = name()]) > 1 and ancestor::*[@AttributeToCheck='true']"> 
     <xsl:attribute name="foo">bar</xsl:attribute>  
     </xsl:if> 
     <xsl:apply-templates select="* | @*"/> 
    </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 
1

你并不需要使用count(),因为空节点集计算为false。只需选择您感兴趣的节点即可:

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 
    <!-- modified identity transform --> 
    <xsl:template match="node() | @*"> 
    <xsl:copy> 
     <!-- check both your conditions --> 
     <xsl:if test=" 
     ancestor::*[@AttributeToCheck = 'true'] 
     and 
     (preceding-sibling::* | following-sibling::*)[name() = name(current())] 
     "> 
     <xsl:attribute name="foo">bar</xsl:attribute> 
     </xsl:if> 
     <xsl:apply-templates select="node() | @*" /> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 
+1

mmh,我不确定你的答案是否符合规则“有相同名称的兄弟元素”。 ../* [name()= name(current())]将只返回一个节点,即当前元素,如果它是唯一的节点。 – Erlock 2009-12-01 14:58:39

+0

@ Erlock:你说的没错。愚蠢的错误,我会纠正它。 – Tomalak 2009-12-01 15:23:02