2009-11-30 55 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