Xpath 1.0を使用して、指定された属性の最大値を返すようにXMLドキュメントをクエリする方法はありますか?
たとえば、最大IDを取得する方法はありますか?
<?xml version="1.0" encoding="utf-8"?>
<library>
<book id="2" name="Dragon Tatoo"/>
<book id="7" name="Ender's Game"/>
<book id="3" name="Catch 22"/>
<book id="1" name="Lord of the rings"/>
</library>
XPath 2.0では、max
関数を使用します。 id
が最も高い本を見つけるには、
/library/book[@id = max(/library/book/@id)]
次のXPathは、IDが最も高い本を選択します。
/library/book[not(@id <= preceding-sibling::book/@id) and not(@id <=following-sibling::book/@id)]
注:以下の情報は、XPath1.0の使用を前提としています。
次の式は、最大のid
値を持つ要素を返します。
_/*/book[not(@id < preceding-sibling::book/@id) and
not(@id < following-sibling::book/@id)]
_
これは、同じ最大値を持つ重複がある場合に複数の要素を返すという点で、@ timboooの回答とは少し異なることに注意してください(@timboooは何も返しません)。この場合、要素が1つだけ必要な場合は、解決戦略が必要です。ドキュメントの順序で最初のそのような要素を選択するには、次を使用します。
_/*/book[not(@id < preceding-sibling::book/@id) and
not(@id < following-sibling::book/@id)][1]
_
最後のものを選択するには、これを使用します:
_/*/book[not(@id < preceding-sibling::book/@id) and
not(@id < following-sibling::book/@id)][last()]
_
このアプローチは、各要素を他のすべての潜在的な最大値と比較する必要があるため、非常に非効率的です(O(n^2)
)。このため、最大の要素を選択するには、ホストプログラミング言語を使用するのがおそらく最善です。最初にすべてのbook
要素を選択してから、そのリストから最大値を選択するだけです。これは(ほとんどの場合)線形演算(O(n)
)であり、非常に大きなドキュメントでは著しく高速になります。たとえば、Java(JAXP)では、次のように実行できます。
_XPath xpath = XPathFactory.newInstance().newXPath();
NodeList nodes = (NodeList) xpath.evaluate("/*/book", doc,
XPathConstants.NODESET);
Node max = nodes.item(0);
for (int i = 0; i < nodes.getLength(); i++) {
int maxval = Integer.parseInt(max.getAttributes()
.getNamedItem("id").getNodeValue());
int curval = Integer.parseInt(nodes.item(i).getAttributes()
.getNamedItem("id").getNodeValue());
if (curval >= maxval)
max = nodes.item(i);
}
System.out.println(max.getAttributes().getNamedItem("name"));
_
これは単なるデモンストレーションであることに注意してください。必要に応じて、nullチェックを必ず含めてください。
これらのツールの実装を特徴とする実装に依存する外部ツールを使用する場合は、 EXSLT:Math function highest()
を試してください。
EXSLTがこれを実装しているという事実は、もちろん、そのような機能がプレーンなxpathで直接利用できないことを意味します。 Transformsを使用していない場合、または純粋に標準に準拠したマークアップを使用したい場合は、他の投稿者の提案をお勧めします。
Lwburkやtimboooのような答えは、1桁しかない数字を表す属性に対してはうまく機能することがわかりました。ただし、属性が複数の桁を持つ数値である場合、属性の値を比較すると、奇妙なことが起こるように見えます。たとえば、元のXMLデータを次のように変更してみてください。
<?xml version="1.0" encoding="utf-8"?>
<library>
<book id="250" name="Dragon Tatoo"/>
<book id="700123" name="Ender's Game"/>
<book id="305" name="Catch 22"/>
<book id="1070" name="Lord of the rings"/>
</library>
提案されたスニペットの実行は機能しません。次のように、id属性に適用されたキャスト演算子xs:int()を使用して解決策を取得しました。
/library/book[not(xs:int(@id) <= preceding-sibling::book/@id) and not(xs:int(@id) <=following-sibling::book/@id)]
それは正しい答えを与えるでしょう!
この例を使用して、最大値を見つけることができます。
XmlDocument doc = new XmlDocument();
doc.Load("../../Employees.xml");
XmlNode node = doc.SelectSingleNode("//Employees/Employee/@Id[not(. <=../preceding-sibling::Employee/@id) and not(. <=../following-sibling::Employee/@Id)]");
int maxId = Convert.ToInt32(node.Value);
Xpathとlinqに関する他の同様のトピックについては、チェックアウトしてください http://rmanimaran.wordpress.com/2011/03/20/xml-find-max-and-min-value-in-a-attribute-using- xpath-and-linq /
XPath 1.0
_/library/book[not(@id < /library/book/@id)]
_
このクエリスタイルはより一般的で、書籍がグループ化されている場合でも機能します。
_<?xml version="1.0" encoding="utf-8"?>
<library>
<genre id="1">
<book id="2" name="Dragon Tatoo"/>
<book id="7" name="Ender's Game"/>
</genre>
<genre id="2">
<book id="3" name="Catch 22"/>
<book id="1" name="Lord of the rings"/>
</genre>
</library>
_
同じクエリが引き続き機能します(パスを変更する必要があります)
_/library/genre/book[not(@id < /library/genre/book/@id)]
_
あるいは
_//book[not(@id < //book/@id)]
_
パフォーマンスの問題を回避するには、代わりにXPath 2 max()
を使用してください