XSLT fleurit avec Java

Avez-vous déjà été déconcerté par un problème de transformation XML difficile que vous ne pouviez pas résoudre avec XSLT (Extensible Stylesheet Language Transformation) seul? Prenez, par exemple, une simple feuille de style de filtre qui sélectionne uniquement les nœuds datant de plus de cinq jours. Vous avez entendu dire que XSLT peut filtrer les documents XML, vous pensez donc que vous résoudrez ce problème en un rien de temps. La première tâche consiste à obtenir la date du jour à partir d'une feuille de style, à condition que les informations ne soient pas incluses dans le document XML d'origine. Malheureusement, vous ne pouvez pas terminer cette tâche avec seulement XSLT. Dans une situation comme celle-ci, vous pouvez simplifier votre code XSLT et résoudre le problème plus rapidement avec une extension Java.

De nombreux processeurs XSLT permettent un certain type de mécanisme d'extension; la spécification les oblige à le faire. Dans le monde de Java et XML, le processeur XSLT le plus utilisé est le processeur open source Apache Xalan. Ecrit en Java, Xalan permet des extensions en Java. De nombreux développeurs trouvent l'extensibilité de Xalan puissante car elle leur permet d'utiliser leurs compétences Java à partir du contexte de la feuille de style. Considérez la façon dont les JSP (JavaServer Pages), les scriptlets et les balises personnalisées ajoutent de la puissance au HTML. Les extensions Xalan ajoutent de la puissance aux feuilles de style de la même manière: en permettant aux développeurs Java d'accéder à leur outil préféré, Java.

Dans cet article, je vais vous montrer comment utiliser Java à partir d'une feuille de style XSLT. Tout d'abord, nous utiliserons l'extensibilité de Xalan pour instancier et utiliser les classes existantes dans le JDK. Plus tard, je vous montrerai comment écrire une fonction d'extension XSLT qui prend un Stringargument et renvoie un fragment DOM (Document Object Model) au processeur de feuille de style.

XSLT est important pour les développeurs J2EE (Java 2 Platform, Enterprise Edition) car le style des documents XML est devenu une opération côté serveur. De plus, JAXP (l'API Java pour le traitement XML), qui inclut la prise en charge des moteurs XSLT, fait désormais partie de la spécification J2EE (J2EE 2.6.11). À ses débuts, XSLT était destiné à styliser XML sur le client; cependant, la plupart des applications stylisent le XML avant de l'envoyer au client. Pour les développeurs J2EE, cela signifie que le processeur XSLT fonctionnera très probablement dans le serveur d'applications.

Avant de continuer avec cet article, sachez que l'utilisation d'extensions Java dans vos feuilles de style XSLT réduira leur portabilité. Alors que les extensions font partie de la spécification XSLT, la façon dont elles sont implémentées ne l'est pas. Si vos feuilles de style s'exécutent sur des processeurs autres que Xalan, tels que le moteur de feuilles de style d'Internet Explorer, vous devez éviter à tout prix d'utiliser des extensions.

Faiblesses XSLT

Parce que XSLT a quelques points faibles, les extensions XSLT s'avèrent très utiles. Je ne dis pas que XSLT est mauvais; cependant, il n'offre tout simplement pas le meilleur outil pour tout traiter dans un document XML. Considérez cette section de XML:

 XSLT n'est pas aussi facile à utiliser que certains le voudraient ...   

Supposons que votre patron vous demande de modifier une feuille de style afin qu'elle convertisse toutes les instances de «n'est pas» en «n'est pas» et localise les étiquettes courantes. XSLT fournit certainement un mécanisme pour faire quelque chose dans ce sens, non? Faux. XSLT ne fournit pas de moyen facile de remplacer l'occurrence d'un mot ou d'un modèle dans une chaîne. Il en va de même pour la localisation. Cela ne veut pas dire que cela ne peut pas être fait avec la syntaxe XSLT standard. Il existe des moyens, mais ils ne sont pas aussi faciles que nous le souhaiterions. Si vous voulez vraiment écrire des fonctions de manipulation de texte à l'aide de modèles récursifs, soyez mon invité.

La principale faiblesse de XSLT est le traitement de texte, ce qui semble raisonnable puisque son objectif est de rendre XML. Cependant, étant donné que le contenu XML est entièrement composé de texte, XSLT a besoin d'une meilleure gestion du texte. Inutile de dire que les concepteurs de feuilles de style nécessitent de temps en temps une certaine extensibilité. Avec Xalan, Java fournit cette extensibilité.

Utiliser les classes JDK dans XSLT

Vous serez peut-être heureux de savoir que vous n'avez pas besoin d'écrire de code Java pour profiter de l'extensibilité de Xalan. Lorsque vous utilisez Xalan, vous pouvez créer et appeler des méthodes sur presque tous les objets Java. Avant d'utiliser une classe Java, vous devez lui fournir un espace de noms XSLT . Cet exemple déclare "java"comme espace de noms pour tout ce qui se trouve dans ou sous le package Java (c'est-à-dire le JDK entier):


  

Maintenant, nous avons besoin de quelque chose à faire. Commençons par un petit document XML:

 Java peut être une mode J. Burke 30/11/97  

On vous a demandé de styliser ce XML afin que le titre apparaisse en majuscules. Un développeur novice en XSLT ouvrirait simplement une référence XSLT pour rechercher la toUpper()fonction; cependant, elle serait déçue de constater que la référence n'en manque pas. La translate()méthode est votre meilleur pari, mais j'ai une méthode encore mieux: java.lang.String.toUpperCase(). Pour utiliser cette méthode, vous devez instancier un Stringobjet avec le contenu du titre. Voici comment créer une nouvelle Stringinstance avec le contenu de l'élément title:


  

L' nameattribut spécifie le handle de votre nouvelle Stringinstance. Vous appelez le constructeur en spécifiant d'abord l'espace de noms avec le chemin restant vers la Stringclasse. Comme vous l'avez peut-être remarqué, il Stringmanque une new()méthode. Vous utilisez new()pour construire un objet Java dans Xalan; il correspond au newmot - clé de Java . Les arguments donnés pour new()déterminer la version du constructeur qui sera appelée. Maintenant que vous avez le contenu du titre dans un Stringobjet Java , vous pouvez utiliser la toUpperCase()méthode, comme ceci:


  

Cela pourrait vous sembler étrange au début. Lorsque vous utilisez des méthodes Java sur une instance particulière, le premier argument est l'instance sur laquelle vous voulez que la méthode soit appelée. De toute évidence, Xalan utilise l'introspection pour fournir cette capacité.

Ci-dessous, vous trouverez une autre astuce. Voici comment vous pouvez émettre la date et l'heure n'importe où dans votre feuille de style en utilisant java.lang.Date:


  

Voici quelque chose qui rendra la journée de quiconque obligé de localiser une feuille de style générique entre deux ou plusieurs langues. Vous pouvez utiliser java.util.ResourceBundlepour localiser du texte littéral dans une feuille de style. Étant donné que votre XML a une balise d'auteur, vous souhaiterez peut-être imprimer à "Author:"côté du nom de la personne.

Une option consiste à créer une feuille de style distincte pour chaque langue, c'est-à-dire une pour l'anglais, une autre pour le chinois, etc. Les problèmes inhérents à cette approche devraient être évidents. La cohérence de plusieurs versions de feuilles de style prend du temps. Vous devez également modifier votre application afin qu'elle choisisse la feuille de style appropriée en fonction des paramètres régionaux de l'utilisateur.

Au lieu de dupliquer la feuille de style pour chaque langue, vous pouvez profiter des fonctionnalités de localisation de Java. La localisation à l'aide d'un ResourceBundleprouve une meilleure approche. Dans XSLT, chargez le ResourceBundleau début de vos feuilles de style, comme ceci:


  

La ResourceBundleclasse s'attend à trouver un fichier appelé General.propertiesdans votre CLASSPATH. Une fois le bundle créé, il peut être réutilisé dans toute la feuille de style. Cet exemple récupère la authorressource:


  

Remarquez à nouveau l'étrange signature de méthode. Normalement, ResourceBundle.getString()ne prend qu'un seul argument; cependant, dans XSLT, vous devez également spécifier l'objet par lequel vous souhaitez appeler la méthode.

Écrivez vos propres extensions

Dans de rares cas, vous devrez peut-être écrire votre propre extension XSLT, sous la forme d'une fonction d'extension ou d'un élément d'extension. Je parlerai de la création d'une fonction d'extension, un concept assez facile à appréhender. Toute fonction d'extension Xalan peut prendre des chaînes en entrée et renvoyer des chaînes au processeur XSLT. Vos extensions peuvent également prendre NodeLists ou Nodes comme arguments et renvoyer ces types au processeur XSLT. L'utilisation de Nodes ou NodeLists signifie que vous pouvez ajouter au document XML d'origine avec une fonction d'extension, ce que nous ferons.

Un type d'élément de texte fréquemment rencontré est une date; il offre une excellente opportunité pour une nouvelle extension XSLT. Notre tâche est de styliser un élément d'article afin que la date s'imprime dans le format suivant:

Vendredi 30 novembre 200

Can standard XSLT complete the date above? XSLT can finish most of the task. Determining the actual day is the difficult part. One way to quickly solve that problem is to use the java.text.SimpleDate format class within an extension function to return a string formatted as we wish. But wait: notice that the day appears in bold text. This returns us to the initial problem. The reason we are even considering an extension function is because the original XML document failed to structure the date as a group of nodes. If our extension function returns a string, we will still find it difficult to style the day field differently than the rest of the date string. Here's a more useful format, at least from the perspective of an XSLT designer:

  11 30 2001  

We now create an XSLT extension function, taking a string as an argument and returning an XML node in this format:

  November 30 Friday 2001  

The class hosting our extension function doesn't implement or extend anything; we will call the class DateFormatter:

public class DateFormatter { public static Node format (String date) {} 

Wow, too easy, huh? There are absolutely no requirements placed on the type or interface of a Xalan extension function. Generally, most extension functions will take a String as an argument and return another String. Other common patterns are to send or receive org.w3c.dom.NodeLists or individual Nodes from an extension function, as we will do. See the Xalan documentation for details on how Java types convert to XSLT types.

In the code fragment above, the format() method's logic breaks into two parts. First, we need to parse the date string from the original XML document. Then we use some DOM programming techniques to create a Node and return it to the XSLT processor. The body of our format() method implementation reads:

 Document doc = DocumentBuilderFactory.newInstance(). newDocumentBuilder().newDocument(); Element dateNode = doc.createElement("formatted-date"); SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, locale); df.setLenient(true); Date d = df.parse(date); df.applyPattern("MMMM"); addChild(dateNode, "month", df.format(d)); df.applyPattern("EEEE"); addChild(dateNode, "day-of-week", df.format(d)); df.applyPattern("yyyy"); dateNode.setAttribute("year", df.format(d)); return dateNode; 

dateNode will contain our formatted date values that we return to the stylesheet. Notice that we've utilized java.text.SimpleDateFormat() to parse the date. This allows us to take full advantage of Java's date support, including its localization features. SimpleDateFormat handles the numeric date conversion and returns month and day names that match the locale of the VM running our application.

Remember: the primary purpose of an extension function is simply to allow us access to existing Java functionality; write as little code as possible. An extension function, like any Java method, can use other methods within the same class. To simplify the format() implementation, I moved repetitive code into a small utility method:

private void addChild (Node parent, String name, String text) { Element child = parent.getOwnerDocument().createElement(name); child.appendChild(parent.getOwnerDocument().createTextNode(text)); parent.appendChild(child); } 

Use DateFormatter within a stylesheet

Now that we have implemented an extension function, we can call it from within a stylesheet. Just as before, we need to declare a namespace for our extension function:


  

Cette fois, nous avons pleinement qualifié le chemin d'accès à la classe hébergeant la fonction d'extension. Ceci est facultatif et dépend du fait que vous utiliserez d'autres classes dans le même package ou juste un seul objet d'extension. Vous pouvez déclarer le full CLASSPATHcomme espace de noms ou utiliser un package et spécifier la classe dans laquelle la fonction d'extension est appelée. En spécifiant le plein CLASSPATH, nous tapons moins lorsque nous appelons la fonction.

Pour utiliser la fonction, appelez-la simplement depuis une selectbalise, comme ceci: