Ответ 1
В XPath 2.0 используйте функцию max
. Чтобы найти книгу с наивысшим id
, сделайте
/library/book[@id = max(/library/book/@id)]
Есть ли способ запросить XML-документ для возврата максимального значения данного атрибута с помощью Xpath 1.0?
Например, есть способ получить максимальный id?
<?xml version="1.0" encoding="utf-8"?>
<library>
<book id="2" name="Dragon Tatoo"/>
<book id="7" name="Ender 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 выбирает книгу с наивысшим идентификатором:
/library/book[not(@id <= preceding-sibling::book/@id) and not(@id <=following-sibling::book/@id)]
Если вы хотите использовать внешние инструменты, которые зависят от вашей реализации с реализацией этих инструментов, попробуйте EXSLT: Math функция highest()
.
Тот факт, что EXSLT реализует это, подразумевает, что такая функция не доступна напрямую в простой xpath, конечно. Если вы не используете Transforms или хотите придерживаться стандартно-совместимой разметки, предложения других плакатов были бы лучшим выбором.
Примечание. Следующая информация предполагает использование XPath 1.0.
Следующее выражение возвращает элемент с наибольшим значением id
:
/*/book[not(@id < preceding-sibling::book/@id) and
not(@id < following-sibling::book/@id)]
Обратите внимание, что это немного отличается от ответа @timbooo, поскольку это возвращает более одного элемента, если есть дубликаты с одинаковым максимальным значением (@timbooo не возвращает ни одного). Если вам нужен только один элемент в этом случае, вам нужна стратегия разрешения. Чтобы выбрать первый такой элемент в заказе документа, используйте это:
/*/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
, а затем выберите max из этого списка. Это (скорее всего) линейная операция (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"));
Обратите внимание, что это всего лишь демонстрация; обязательно включите нулевые проверки, если это необходимо.
Я нашел, что ответы вроде lwburk или timbooo отлично работают для атрибутов, представляющих числа, имеющие только одну цифру. Однако, если атрибут представляет собой число, имеющее более одной цифры, при сравнении значений атрибутов происходит нечто лишнее. Например, попробуйте изменить исходные данные XML примерно так:
<?xml version="1.0" encoding="utf-8"?>
<library>
<book id="250" name="Dragon Tatoo"/>
<book id="700123" name="Ender Game"/>
<book id="305" name="Catch 22"/>
<book id="1070" name="Lord of the rings"/>
</library>
Выполнение предложенных фрагментов не будет работать. Я получил решение, используя оператор casting xs: int(), примененный к атрибуту id, например:
/library/book[not(xs:int(@id) <= preceding-sibling::book/@id) and not(xs:int(@id) <=following-sibling::book/@id)]
Это даст правильный ответ!
Этот пример можно использовать для поиска max.
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 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()