XSLT Décimal mise en forme basée sur XML d'entrée
J'ai une situation où mes fichiers XSLT doit afficher les prix alongwith décimales, sous certaines conditions, selon que l'entrée XML contient des décimales ou pas. Donc, je peux recevoir des fichiers XML avec deux types de valeurs - XML contiendra tous les prix mis en forme avec des décimales jusqu'à deux endroits (j'appelle cela une "Virgule-XML") ou les prix seront arrondis à l'entier le plus proche (j'appelle cela un "Integer-XML").
Mon problème est que j'ai besoin de revoir aussi peu que possible dans les fichiers XSLT et pourtant leur permettre d'appliquer la transformation en XHTML dans le même format que le XML d'entrée. Pour ce faire, j'ai mis en place et a proposé trois lignes directrices pour mon équipe:
- Supprimer tous les
format-number()
appels de fonction lorsque la valeur est calculée pour les calculs ou stockée dans une variable. Utilisationnumber(<value>)
à la place. Cependant, certaines conditions s'appliquent à cette règle (Voir ci-dessous). - Lorsque la valeur est affichée, utilisez les
format-number(<value>, '#.##')
format. Cela devrait garantir que nombre entier ou décimal de l'affichage de valeurs comme initialement présents dans le XML. - Pour les balises facultatives (comme la "Réduction"), utilisez la
format-number(<value>, '0.00')
fonction, même si la valeur est calculée. Cela est nécessaire parce que si la balise est absent, en essayant d'obtenir une valeur donnera uneNaN
résultat.
Ici est un exemple illustratif de la transformation XSLT:
<x:stylesheet version="1.0" xmlns:x="http://www.w3.org/1999/XSL/Transform">
<x:template match="/">
<html>
<body>
<table border="1" width="60%">
<tr>
<th>Simple</th>
<th>number()</th>
<th>format-number(<expression>, '0.00')</th>
<th>format-number(<expression>, '#.##')</th>
</tr>
<x:apply-templates />
</table>
</body>
</html>
</x:template>
<x:template match="Item">
<x:variable name="qty" select="number(@numItems)" />
<x:variable name="cost" select="number(ItemCost) * $qty" />
<x:variable name="extraCharges" select="(number(Tax) + number(TxnFee)) * $qty"/>
<x:variable name="discount" select="format-number(Discount, '0.00') * $qty"/>
<tr>
<td>
<!-- Works for Integer-XML, but values in Decimal-XML are
*sometimes* rendered upto 14 decimal places. Even though Quickwatch
shows it correctly in the debugger in VS. I cannot figure out what's
special about the error cases. -->
<x:value-of select="$cost + $extraCharges - $discount"/>
</td>
<td>
<!-- Works same as the above case. -->
<x:value-of select="number($cost + $extraCharges - $discount)"/>
</td>
<td>
<!-- Works for Decimal-XML, but values in Integer-XML are always
rendered with decimal digits. -->
<x:value-of select="format-number(($cost + $extraCharges - $discount), '0.00')"/>
</td>
<td>
<!-- Works for Integer-XML, but some values in Decimal-XML are
rendered incorrectly. For example, 95.20 is rendered as 95.2;
95.00 is rendered as 95 -->
<x:value-of select="format-number(($cost + $extraCharges - $discount), '#.##')"/>
</td>
</tr>
</x:template>
</x:stylesheet>
Que les commentaires HTML remarque, il fonctionne dans la plupart des cas, mais pas tous.
Je voudrais utiliser une expression unique de formater tous les prix de même que l'entrée, plutôt que d'appliquer "lorsque-dans le cas contraire" des constructions partout, ce qui serait en outre exiger un booléen en paramètre passé à la transformation XSLT pour déterminer le format d'affichage. L'état actuel de la transformation XSLT est que tous les nombres sont arrondis, quelle que soit l'entrée, à l'aide format-number(<expression>, '0')
.
Comment puis-je y arriver?
Edit: Après Dimitre commentaire, j'ai décidé de créer un exemple de XML et XSLT-dessus), de sorte que les experts peuvent essayer facilement.
XML exemple (celui-ci contient des décimales):
<ShoppingList>
<Item numItems="2">
<ItemCost>10.99</ItemCost>
<Tax>3.99</Tax>
<TxnFee>2.99</TxnFee>
<Discount>2.99</Discount>
</Item>
<Item numItems="4">
<ItemCost>15.50</ItemCost>
<Tax>5.50</Tax>
<TxnFee>3.50</TxnFee>
<Discount>3.50</Discount>
</Item>
</ShoppingList>
- Vous avez oublié d'indiquer, même la plus simple possible document XML. Ce n'est pas utile.
- Je n'ai pas oublié de le fournir, mais je dois avouer que je ne savais pas qu'une source XML serait nécessaire parce que ce n'est pas vraiment une transformation de la question. C'est une question générique décimal mise en forme. Encore, je vais essayer d'en créer un si cela aide.
Vous devez vous connecter pour publier un commentaire.
Si je vous comprends bien, vous souhaitez:
Le plus proche que vous avez obtenu ce que vous voulez est de cette expression:
Puisqu'il n'est pas facile à utiliser l'expression conditionnelle dans XPath 1.0 (XPath 2.0 a
if/then/else
), un compromis serait celui-ci:Cependant, cet "échec" (couper les décimales) lorsque
$cost
,$extraCharges
et$discount
ajouter jusqu'à un chiffre rond.Le peu attrayant alternative est d'utiliser Becker méthode (nommée d'après Oliver Becker de HU Berlin):
Techniquement, c'est un one-liner. Les deux
sub-string()
parties sont mutuellement exclusifs basés sur$condition
, doncconcat()
ne retourner une valeur ou l'autre.Pratiquement (en particulier pour votre cas), ça va être un bordel ingérable, lent et entièrement dissimuler l'intention:
Puisque vous avez déclaré que toutes les valeurs d'entrée contenues décimales, ou aucun d'entre eux, l'expression ci-dessus vérifie seulement si
$cost
contient le point décimal. Il serait bon de vérifier$extraCharges
et$discount
ainsi, je suppose.