Как найти атрибут max из документа XML с помощью Xpath 1.0

Есть ли способ запросить 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>

Ответ 1

В XPath 2.0 используйте функцию max. Чтобы найти книгу с наивысшим id, сделайте

/library/book[@id = max(/library/book/@id)]

Ответ 2

Следующий XPath выбирает книгу с наивысшим идентификатором:

/library/book[not(@id <= preceding-sibling::book/@id) and not(@id <=following-sibling::book/@id)]

Ответ 3

Если вы хотите использовать внешние инструменты, которые зависят от вашей реализации с реализацией этих инструментов, попробуйте EXSLT: Math функция highest().

Тот факт, что EXSLT реализует это, подразумевает, что такая функция не доступна напрямую в простой xpath, конечно. Если вы не используете Transforms или хотите придерживаться стандартно-совместимой разметки, предложения других плакатов были бы лучшим выбором.

Ответ 4

Примечание. Следующая информация предполагает использование 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"));

Обратите внимание, что это всего лишь демонстрация; обязательно включите нулевые проверки, если это необходимо.

Ответ 5

Я нашел, что ответы вроде 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)]

Это даст правильный ответ!

Ответ 6

Этот пример можно использовать для поиска 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/

Ответ 7

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()