Cómo encontrar el atributo máximo de un documento XML usando Xpath 1.0
xpath-1.0 (6)
¿Hay alguna manera de consultar un documento XML para devolver el máximo de un atributo determinado utilizando Xpath 1.0?
Por ejemplo, ¿hay alguna forma de obtener el ID máximo?
<?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>
En XPath 2.0, use la función max
. Para encontrar el libro con la id
más alta, hazlo
/library/book[@id = max(/library/book/@id)]
Este ejemplo se puede usar para encontrar el máximo.
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);
Para otros temas similares sobre xpath y linq echa un vistazo a http://rmanimaran.wordpress.com/2011/03/20/xml-find-max-and-min-value-in-a-attribute-using-xpath-and- linq /
Si está dispuesto a utilizar herramientas externas, que depende de su implementación con implementaciones de estas herramientas, pruebe la función EXSLT: Math highest()
.
El hecho de que EXSLT implemente esto implica que tal característica no está directamente disponible en plain xpath, por supuesto. Si no está utilizando Transformaciones, o quiere adherirse exclusivamente al marcado que cumpla con los estándares, las sugerencias de otros carteles serían una mejor opción.
Nota: La siguiente información supone el uso de XPath 1.0.
La siguiente expresión devuelve los elementos con el mayor valor de id
:
/*/book[not(@id < preceding-sibling::book/@id) and
not(@id < following-sibling::book/@id)]
Tenga en cuenta que esto es ligeramente diferente de la respuesta de @miebooo porque devolverá más de un elemento cuando haya duplicados con el mismo valor máximo (@ timbooo''s no devolverá ninguno). Si solo quieres un elemento en este caso, entonces necesitas una estrategia de resolución. Para elegir el primer elemento en orden de documento, use esto:
/*/book[not(@id < preceding-sibling::book/@id) and
not(@id < following-sibling::book/@id)][1]
Para elegir el último, usa esto:
/*/book[not(@id < preceding-sibling::book/@id) and
not(@id < following-sibling::book/@id)][last()]
Este enfoque es muy ineficiente ( O(n^2)
) porque requiere que compare cada elemento con cada otro potencial máximo. Por este motivo, probablemente sea mejor usar el lenguaje de programación de su host para seleccionar el elemento máximo. Simplemente seleccione todos los elementos del book
primero y luego elija el máximo de esa lista. Esto es (muy probablemente) una operación lineal ( O(n)
), que sería notablemente más rápida en documentos muy grandes. Por ejemplo, en Java (JAXP) puede hacerlo así:
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"));
Tenga en cuenta que esto es solo una demostración; asegúrese de incluir null-checks cuando corresponda.
He encontrado que respuestas como la de lwburk o timbooo funcionan bien para atributos que representan números que tienen solo un dígito. Sin embargo, si el atributo es un número que tiene más de un dígito, cosas extrañas parecen suceder cuando se comparan los valores de los atributos. Por ejemplo, intente cambiar los datos XML originales con algo como esto:
<?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>
La ejecución de los fragmentos sugeridos no funcionará. Obtuve una solución usando el operador de casting xs: int () aplicado en el atributo id, como en:
/library/book[not(xs:int(@id) <= preceding-sibling::book/@id) and not(xs:int(@id) <=following-sibling::book/@id)]
¡Eso dará la respuesta correcta!
El siguiente XPath selecciona el libro con mayor ID:
/library/book[not(@id <= preceding-sibling::book/@id) and not(@id <=following-sibling::book/@id)]