使用此源文档:

<?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>

我想将foo=“bar”属性添加到以下两个元素:
具有同名的兄弟元素
具有属性attributetocheck的任何祖先
结果应该是:
<?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表达式?

最佳答案

我认为这是xsl处理器而不是xslt的问题。
(您可能会注意到,除了count(ancestor::*[@AttributeToCheck]) > 0之外,应该是这样的ancestor::*[@AttributeToCheck='true']。)
我尝试了以下操作,它似乎在xmlstarlet上工作正常:

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

根据我对xpath 1.0规范的第2.1条和第3.4条的解释,您的表达式和上面的表达式在这种情况下(至少)具有相同的效果。
另一个解决方案是使用模式模板。
如果@AttributeToCheck可以嵌套,这将非常有用。

08-16 22:15