2010-08-30 16 views
1

Je le xml simple:Shortest XPath pour trouver minimum/maximum de nœuds datetime fratrie

<root> 
<item> 
    <d>2002-05-30T09:00:00</d> 
</item> 
<item> 
    <d>2005-05-30T09:00:00</d> 
</item> 
<item> 
    <d>2003-05-30T09:00:00</d> 
</item> 
</root> 

Maintenant, je veux trouver le noeud dateTime minimum ou maximum en utilisant XPath.

Ma solution au moment est:

/root/item[not(number(translate(./d, 'TZ:-', '.')) <= number(translate(following-sibling::item, 'TZ:-', '.')))][not(number(translate(./d, 'TZ:-', '.')) <= number(translate(preceding-sibling::item, 'TZ:-', '.')))][1]/d 

Il fonctionne mais est laid comme l'enfer et pas très efficace. Fondamentalement, il convertit le dateTime en un nombre, puis les compare entre eux. Je l'ai adapté de here.

Quelle est la meilleure façon de faire cela?

Vive

néo

Répondre

11

Vous ne pouvez en XPath 1.0 si vous ne saurez pas à l'avance le nombre de item parce que chaque fonction Wich a aucun argument node-set jeté son argument en prenant le premier noeud node-set, et l'opérateur de comparaison d'ordre ne fonctionne pas avec les chaînes.

Dans XPath 2.0, vous pouvez utiliser:

max(/root/item/d/xs:dateTime(.)) 
+0

Fonctionne très bien, j'aime beaucoup cette syntaxe, voir aussi mon commentaire sur l'autre réponse. – letmaik

+0

@neo: Je suis content que cela vous ait aidé. Maintenant que vous savez que vous avez XQuery 1.0 et XPath 2.0, vous trouverez beaucoup d'améliorations de XPath 1.0: dans ce cas non seulement 'fn: max' mais aussi la possibilité d'utiliser les expressions comme étape finale dans le chemin. J'ai aussi réessayé ta question. –

2

@neo, l'expression XPath vous listez ne fonctionne pas quand je le tester. Essayez un ensemble de données différentes et vous verrez:

<root> 
    <item> 
     <d>2003-05-30T09:00:00</d> 
    </item> 
    <item> 
     <d>2002-05-30T09:00:00</d> 
    </item> 
    <item> 
     <d>2005-05-30T09:00:00</d> 
    </item> 
</root> 

Votre XPath produit 2003-05-30T09:00:00, ce qui est évidemment pas le maximum.

Et il est logique que cela ne fonctionne pas, car les axes suivants-sibling :: et following-sibling :: dans les fonctions translate() ne donneront qu'un seul frère. Vous essayez d'effectuer une comparaison générale (ensemble) sur tous les frères et soeurs de chaque axe, mais le premier argument de translate() doit être converti en une chaîne, avant que l'opérateur de comparaison général ait une chance de faire son truc. Converting a nodeset to a string ignores all nodes except the first one in document order.

En outre, translate(./d, 'TZ:-', '.') vous donne des résultats comme 2003.05.30.09.00.00. Ce n'est pas un nombre valide, au-delà du '5'. Vos données de test ne fonctionnent que parce que les années sont toutes différentes. Vous obtiendriez de meilleurs résultats avec translate(./d, 'TZ:-', '') ce qui donnerait 20030530090000. Alejandro dit qu'il n'est pas possible de faire cela dans XPath 1.0, et il a peut-être raison. Essayons, et peut-être que nous apprendrons quelque chose même si nous n'y parvenons pas.

Ensuite, j'essaierais d'utiliser la comparaison générale en dehors de la fonction de traduction, afin de pouvoir comparer des ensembles de nœuds entiers. Quelque chose comme cette tentative naïve:

/root/item[ 
    not(following-sibling::item[ 
     translate($current/d, 'TZ:-', '') &lt;= translate(./d, 'TZ:-', '')]) 
    and not(preceding-sibling::item[ 
     translate($current/d, 'TZ:-', '') &lt;= translate(./d, 'TZ:-', '')])] 

Cependant elle est incomplète comme le montre le courant de $ pseudo-variable, qui est censé se référer à l'élément le plus extérieur, celui qui est le nœud de contexte extérieur tous les prédicats. Malheureusement, XPath 1.0 ne nous permet pas de faire référence à ce contexte externe lorsqu'un autre contexte a été poussé sur la pile par un prédicat interne.(Il semble que certaines implémentations de XSLT, comme MSXML, vous permettent de faire cela en utilisant une fonction étendue comme current(1), mais je ne trouve pas d'informations à ce sujet pour le moment. Une solution XPath, et current() n'est pas XPath.)

À ce stade, je vais être d'accord avec Alejandro que c'est impossible en XSLT 1.0 standard pur.

Si vous spécifiez l'environnement dans lequel vous utilisez XPath, par ex. XSLT, ou Javascript, ou XQuery, nous pouvons probablement vous suggérer un moyen efficace d'obtenir ce dont vous avez besoin. Si c'est XPath 2.0, Alejandro a votre réponse.

Si vous avez XQuery 1.0, il devrait soutenir XPath 2.0, vous pouvez utiliser la solution d'Alejandro, avec doc() pour accéder à votre document XML d'entrée:

max(doc("myInput.xml")/root/item/d/xs:dateTime(.)) 
+0

@larsH: l'expression XPath 1.0 maximale générale '$ nodes [not ($ nodes>.)]] Ne peut pas être utilisée ici car ces valeurs de chaîne ne peuvent pas être converties en nombre. Si vous utilisez 'fn: translate' alors vous devez effectuer l'appel de fonction pour chaque noeud de l'ensemble de noeuds (la séquence est un type de XSLT 2.0), vous devez donc connaître le compte' item' à l'avance: '/ root/item [ . > = translate (../ item [1], 'TZ: -', '')] [. > = translate (../ item [2], 'TZ: -', '')] [. > = translate (../ item [3], 'TZ: -', '')] ' –

+0

Je dois dire que j'aime vraiment la version XPath 2.0, mais je n'ai que XPath 1.0 disponible. Bien que cela signifie plus de travail dans mon cas, je peux aussi utiliser XQuery ici. Comment cela ressemblerait-il? – letmaik

+0

Si vous pouvez utiliser XQuery, il devrait contenir XPath 2.0. Vous pouvez donc utiliser la solution d'Alejandro: 'max (doc (" myInput.xml ")/root/item/d/xs: dateTime (.))' Disclaimer: Je ne suis pas un utilisateur de XQuery. – LarsH

0

Il a travaillé pour moi, mais je suis à la recherche une amélioration xpath maintenant ie pour les données ci-dessous:

<root> 
<items> 
    <item> 
    <d>2002-05-30T09:00:00</d> 
    </item> 
</items> 
<items> 
    <item> 
    <d>2005-05-30T09:00:00</d> 
    </item> 
</items> 
<items> 
    <item> 
    <d>2005-05-30T10:00:00</d> 
    </item> 
</items> 
</root>