Cette feuille de style:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="map"
exclude-result-prefixes="m">
<xsl:key name="kTestIntID" match="Book"
use="number(ID)=number(ID) and not(contains(ID,'.'))"
m:message="Books with no integer ID"/>
<xsl:key name="kTestFloatPrice" match="Book"
use="number(Price)=number(Price) and contains(Price,'.')"
m:message="Books with no float Price"/>
<xsl:key name="kTestEmptyElement" match="Book"
use="not(*[not(node())])"
m:message="Books with empty element"/>
<xsl:key name="kTestAllElements" match="Book"
use="ID and Name and Price and Country"
m:message="Books with missing element"/>
<xsl:key name="kBookByID" match="Book" use="ID"/>
<m:map from="US" to="United States"/>
<m:map from="CA" to="Canada"/>
<xsl:variable name="vCountry" select="document('')/*/m:map"/>
<xsl:variable name="vKeys" select="document('')/*/xsl:key/@name
[starts-with(.,'kTest')]"/>
<xsl:variable name="vTestNotUniqueID"
select="*/*[key('kBookByID',ID)[2]]"/>
<xsl:template match="/" name="validation">
<xsl:param name="pKeys" select="$vKeys"/>
<xsl:param name="pTest" select="$vTestNotUniqueID"/>
<xsl:param name="pFirst" select="true()"/>
<xsl:choose>
<xsl:when test="$pTest and $pFirst">
<html>
<body>
<xsl:if test="$vTestNotUniqueID">
<h2>Books with no unique ID</h2>
<ul>
<xsl:apply-templates
select="$vTestNotUniqueID"
mode="escape"/>
</ul>
</xsl:if>
<xsl:variable name="vCurrent" select="."/>
<xsl:for-each select="$vKeys">
<xsl:variable name="vKey" select="."/>
<xsl:for-each select="$vCurrent">
<xsl:if test="key($vKey,'false')">
<h2>
<xsl:value-of
select="$vKey/../@m:message"/>
</h2>
<ul>
<xsl:apply-templates
select="key($vKey,'false')"
mode="escape"/>
</ul>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</body>
</html>
</xsl:when>
<xsl:when test="$pKeys">
<xsl:call-template name="validation">
<xsl:with-param name="pKeys"
select="$pKeys[position()!=1]"/>
<xsl:with-param name="pTest"
select="key($pKeys[1],'false')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Book" mode="escape">
<li>
<xsl:call-template name="escape"/>
</li>
</xsl:template>
<xsl:template match="*" name="escape" mode="escape">
<xsl:value-of select="concat('<',name(),'>')"/>
<xsl:apply-templates mode="escape"/>
<xsl:value-of select="concat('</',name(),'>')"/>
</xsl:template>
<xsl:template match="text()" mode="escape">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
<!-- Up to here, rules for validation.
From here, rules for transformation -->
<xsl:template match="@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Country/text()">
<xsl:variable name="vMatch"
select="$vCountry[@from=current()]"/>
<xsl:choose>
<xsl:when test="$vMatch">
<xsl:value-of select="$vMatch/@to"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Price[. > 20]">
<xsl:call-template name="identity"/>
<Expensive>True</Expensive>
</xsl:template>
</xsl:stylesheet>
Avec votre entrée, sortie:
<html>
<body>
<h2>Books with no unique ID</h2>
<ul>
<li><Book><ID>1</ID><Name>Book1</Name><Price>24.??</Price><Country>US</Country></Book></li>
<li><Book><ID>1</ID><Name></Name><Price>24.69</Price></Book></li>
</ul>
<h2>Books with no float Price</h2>
<ul>
<li><Book><ID>1</ID><Name>Book1</Name><Price>24.??</Price><Country>US</Country></Book></li>
</ul>
<h2>Books with empty element</h2>
<ul>
<li><Book><ID>1</ID><Name></Name><Price>24.69</Price></Book></li>
</ul>
<h2>Books with missing element</h2>
<ul>
<li><Book><ID>1</ID><Name></Name><Price>24.69</Price></Book></li>
</ul>
</body>
</html>
Avec l'entrée correcte:
<Books>
<Book>
<ID>1</ID>
<Name>Book1</Name>
<Price>19.50</Price>
<Country>US</Country>
</Book>
<Book>
<ID>2</ID>
<Name>Book2</Name>
<Price>24.69</Price>
<Country>CA</Country>
</Book>
</Books>
Sortie:
<Books>
<Book>
<ID>1</ID>
<Name>Book1</Name>
<Price>19.50</Price>
<Country>United States</Country>
</Book>
<Book>
<ID>2</ID>
<Name>Book2</Name>
<Price>24.69</Price>
<Expensive>True</Expensive>
<Country>Canada</Country>
</Book>
</Books>
Remarque: utilisation des touches pour des performances. Ceci est une preuve de concept. Dans la vie réelle, la sortie XHTML doit être enveloppée dans une instruction xsl:message
. De http://www.w3.org/TR/xslt#message
L'instruction xsl: message envoie un message d'une manière qui dépend de le processeur XSLT.Le contenu de la instruction xsl: message est un modèle. Le xsl: message est instancié par instancier le contenu pour créer un fragment XML . Ce fragment XML est le contenu du message. REMARQUE: Un processeur XSLT peut implémenter le message xsl: en ouvrant une boîte d'alerte ou en écrivant dans un fichier journal.
Si l'attribut fin a la valeur oui, alors le processeur XSLT doit terminer le traitement après l'envoi du message. La valeur par défaut est non.
Modifier: code de compactage et problème de carte de pays d'adressage.
Edit 2: Dans la vraie vie, avec de grands documents XML et d'autres outils de ENTERPRICE, la meilleure approche serait d'exécuter la transformation avec le processeur de schéma courant XSLT 2.0 pour valider ou exécuter la validation independly avec bien savoir schéma validateurs. Si pour une raison quelconque de ces choix ne sont pas aviable, ne vont pas avec ma preuve de concept réponse, car ayant des clés pour chaque règle de validation font cause beaucoup d'utilisation de la mémoire pour les grands documents. Le meilleur moyen pour le dernier cas est d'ajouter des règles pour intercepter les erreurs de validation terminant la transformation avec un message. A titre d'exemple, cette feuille de style:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="map"
exclude-result-prefixes="m">
<xsl:key name="kIDByValue" match="ID" use="."/>
<m:map from="US" to="United States"/>
<m:map from="CA" to="Canada"/>
<xsl:variable name="vCountry" select="document('')/*/m:map"/>
<xsl:template name="location">
<xsl:param name="pSteps" select="ancestor-or-self::*"/>
<xsl:if test="$pSteps">
<xsl:call-template name="location">
<xsl:with-param name="pSteps"
select="$pSteps[position()!=last()]"/>
</xsl:call-template>
<xsl:value-of select="concat('/',
name($pSteps[last()]),
'[',
count($pSteps[last()]/
preceding-sibling::*
[name()=
name($pSteps[last()])])
+1,
']')"/>
</xsl:if>
</xsl:template>
<xsl:template match="ID[not(number()=number() and not(contains(.,'.')))]">
<xsl:message terminate="yes">
<xsl:text>No integer ID at </xsl:text>
<xsl:call-template name="location"/>
</xsl:message>
</xsl:template>
<xsl:template match="Price[not(number()=number() and contains(.,'.'))]">
<xsl:message terminate="yes">
<xsl:text>No float Price at </xsl:text>
<xsl:call-template name="location"/>
</xsl:message>
</xsl:template>
<xsl:template match="Book/*[not(node())]">
<xsl:message terminate="yes">
<xsl:text>Empty element at </xsl:text>
<xsl:call-template name="location"/>
</xsl:message>
</xsl:template>
<xsl:template match="Book[not(ID and Name and Price and Country)]">
<xsl:message terminate="yes">
<xsl:text>Missing element at </xsl:text>
<xsl:call-template name="location"/>
</xsl:message>
</xsl:template>
<xsl:template match="ID[key('kIDByValue',.)[2]]">
<xsl:message terminate="yes">
<xsl:text>Duplicate ID at </xsl:text>
<xsl:call-template name="location"/>
</xsl:message>
</xsl:template>
<!-- Up to here, rules for validation.
From here, rules for transformation -->
<xsl:template match="@*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Country/text()">
<xsl:variable name="vMatch"
select="$vCountry[@from=current()]"/>
<xsl:choose>
<xsl:when test="$vMatch">
<xsl:value-of select="$vMatch/@to"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Price[. > 20]">
<xsl:call-template name="identity"/>
<Expensive>True</Expensive>
</xsl:template>
</xsl:stylesheet>
Avec votre entrée, ce message arrête la transformation:
Duplicate ID ar /Books[1]/Book[1]/ID[1]
Avec l'entrée correcte, émet le même que précédemment.
vache sainte, je ne vais pas faire semblant de comprendre tout cela encore (qui ressemble à XSLT est un artform aquired!), Mais il fait exactement ce que nous voulons pas trop de lignes! Une question supplémentaire: si le code de pays est une liste de recherche, par ex. US = États-Unis, CA = Canada, le feriez-vous toujours de la même façon? –
@Andre White: Voir ma modification adressant votre demande et une suggestion plus réelle de la vie. –