2

xpathを介して、refNodeから到達可能なすべてのノードで何らかの操作を実行する次のc#メソッドがあります

void foo(XmlNode refNode, string xpath)
{
    XmlNodeList list=refNode.SelectNodes(xpath);
    //perform operation on each element of the list
}

私が取得している入力xmlの1つは次のとおりです。

<A>
    <B>***
        <C>
                  <B>One</B>
            </C>
        <B>
                  <B>Two</B>
            </B>
    </B>
    <B>...</B>
    <B>...</B>
</A>

ここで、refNode <B>(マーク付き) を選択し、refNode のすべての子孫ノードを選択するが、他のノード内にネストされていない***xpath を使用して foo() に渡す必要があります。<B><B>

たとえば、指定された入力では、結果に次のものが含まれている必要があります。

1. <B>One</B>
2. <B><B>Two</B></B>

3 つの結果を返す .//B と 0 の結果を返す .//B[not(ancesotr::B)] を試しました。

目的の結果を得るには、どの Xpath を使用すればよいですか?

編集

メソッド foo は変更できますが、そのシグネチャは変更できません。このメソッドはライブラリの一部であり、少数のユーザーによって使用されています。上記の入力は特定のインスタンスにすぎません。ユーザーはノード A を refnode として送信し、すべての C ノードを要求することもできます。

Edit2 @Dimitre Novatchevのソリューションは、署名を変更せずにfoo内のrefnodeのxpathを取得できる場合、またはthisノード、つまりxpathが適用されているノードを指定する方法がある場合に機能します。

.//B[not(ancesotr::B) or ancesotr::B[1]=**this**]
4

4 に答える 4

2

この純粋なXPath1.0式を使用します

$vrefNode/descendant::B[count(ancestor::B) - count($vrefNode/ancestor::B) = 1]

ここで$vrefNode、「参照ノード」を選択するXPath式で(変数参照を使用できる場合を除いて)置き換える必要があります。

XSLTベースの検証

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:copy-of select=
  "/*/B[1]/descendant::B[count(ancestor::B) - count(/*/B[1]/ancestor::B) = 1]"/>
 </xsl:template>
</xsl:stylesheet>

この変換が提供されたXMLドキュメントに適用される場合:

<A>
    <B>***
        <C>
            <B>One</B>
        </C>
        <B>
            <B>Two</B>
        </B>
    </B>
    <B>...</B>
    <B>...</B>
</A>

XPath式が評価され、この評価によって選択された要素が出力にコピーされます

<B>One</B>
<B>

   <B>Two</B>

</B>
于 2012-12-29T17:57:44.823 に答える
0

純粋な XPath でそれができるかどうかはわかりません。XQuery にはより表現力があります。たとえば、変数内のさらに子孫を検索するサブツリーletの要素を保持するために簡単に使用でき、それが唯一の祖先であることを確認できます。XQuery の例を次に示します。BBB

let $inp := <A>
  <B>
    ***
    <C>
      <B>One</B>
    </C>
    <B>
      <B>Two</B>
    </B>
    <D>
      <E>
        <B>Three</B>
      </E>
    </D>
    <D>
      <B>Four<Foo>
        <B>Five</B>
      </Foo>
    </B>
    </D>
  </B>
  <B>...</B>
  <B>...</B>
</A>
let $b := $inp/B[1]
return $b/descendant::B[count(ancestor::B) = 1 and ancestor::B[1] is $b]

それはノードを見つけます

<B>One</B>
<B>
<B>Two</B>
</B>
<B>Three</B>
<B>Four<Foo>
<B>Five</B>
</Foo>
</B>

XmlPrime、Saxon 9 .NET バージョン、AltovaXML、またはhttp://qm.codeplex.com/のような .NET フレームワーク用の XQuery 実装があります。

于 2012-12-29T10:11:45.600 に答える
0

refNode のすべての子孫<B>ノードを選択するが、他の<B>ノード内にネストされていない xpath

XSLTでは、使用できます

.//B[generate-id(ancestor::B[1]) = generate-id(current())]

これにより、現在のコンテキスト ノードのすべての子孫が選択されます。そのB子孫のうち、最も近い囲んでいるB祖先は、現在のコンテキスト ノード自体です。しかし、これは XSLT 固有であり、単純な XPath の一部ではないgenerate-id()およびに依存しています。current()

以下は、2 段階の代替手段です。

foo(refNode, ".//B[count(ancestor::B) = " + refNode.SelectNodes("ancestor-or-self::B").Count + "]");

XPath 式を動的に生成します。これは、同じ数の先祖を持つすべてのB子孫を見つけます(それ自体が要素である場合は 1 を加えた数)。これにより、複数の「B のレイヤー」の深さの子孫を除外する効果があります。refNodeBrefNoderefNodeBB

于 2012-12-29T10:24:04.480 に答える
0

XPath を使用する必要がありますか? Linq-to-XML を使用できる場合は、

XElement refNode = ...
XElement wantedBs = refNode.Descendants("B")
                           .Where(b => parent.Name.LocalName != "B");
于 2012-12-30T03:18:14.813 に答える