Programmering

XSLT blomstrer med Java

Er du nogensinde blevet stumpet af et vanskeligt XML-transformationsproblem, som du ikke kunne løse med XSLT (Extensible Stylesheet Language Transformation) alene? Tag for eksempel et simpelt filterformatark, der kun vælger dem noder dateret tidligere end for fem dage siden. Du har hørt, at XSLT kan filtrere XML-dokumenter, så du finder ud af, at du løser dette problem på ingen tid. Den første opgave er at hente dagens dato fra et stilark, forudsat at oplysninger ikke er inkluderet i det originale XML-dokument. Desværre kan du ikke fuldføre denne opgave med kun XSLT. I en situation som denne kan du forenkle din XSLT-kode og løse problemet hurtigere med en Java-udvidelse.

Mange XSLT-processorer muliggør en eller anden form for udvidelsesmekanisme; specifikationen kræver, at de gør det. I en verden af ​​Java og XML er den mest anvendte XSLT-processor open source Apache Xalan-processor. Xalan er skrevet i Java og giver mulighed for udvidelser i Java. Mange udviklere finder Xalans udvidelighed kraftig, fordi den lader dem udnytte deres Java-færdigheder inden for stilarkkonteksten. Overvej, hvordan JSP'er (JavaServer Pages), scriptlets og brugerdefinerede tags føjer HTML til HTML. Xalan-udvidelser tilføjer strøm til stilark på stort set samme måde: ved at give Java-udviklere adgang til deres yndlingsværktøj, Java.

I denne artikel vil jeg demonstrere, hvordan du kan bruge Java fra et XSLT-stilark. Først vil vi bruge Xalans udvidelighed til at instantiere og bruge eksisterende klasser inden for JDK. Senere viser jeg dig, hvordan du skriver en XSLT-udvidelsesfunktion, der tager en Snor argument og returnerer et DOM (Document Object Model) fragment til stilarkprocessoren.

XSLT er vigtigt for J2EE (Java 2 Platform, Enterprise Edition) udviklere, fordi styling af XML-dokumenter er blevet en server-side operation. Også JAXP (Java API til XML Processing), som inkluderer support til XSLT-motorer, er blevet en del af J2EE-specifikationen (J2EE 2.6.11). I sin barndom var XSLT beregnet til at style XML på klienten; de fleste applikationer styler imidlertid XML, før de sendes til klienten. For J2EE-udviklere betyder det, at XSLT-processoren sandsynligvis kører inden for app-serveren.

Inden du fortsætter med denne artikel, skal du advares om, at brug af Java-udvidelser i dine XSLT-stilark vil reducere deres bærbarhed. Mens udvidelser er en del af XSLT-specifikationen, er det ikke den måde, de implementeres på. Hvis dine typografiark kører på andre processorer end Xalan, såsom Internet Explorer's stilarkmotor, bør du undgå at bruge udvidelser for enhver pris.

XSLT svagheder

Fordi XSLT har nogle svage pletter, viser XSLT-udvidelser sig meget nyttige. Jeg siger ikke, at XSLT er dårlig; dog tilbyder det bare ikke det bedste værktøj til behandling af alt i et XML-dokument. Overvej dette afsnit af XML:

 XSLT er ikke så let at bruge som nogle ville have dig ... 

Antag, at din chef beder dig om at ændre et typografiark, så det konverterer alle forekomster af "er ikke" til "er ikke" og lokaliserer almindelige etiketter. Bestemt XSLT giver en mekanisme til at gøre noget i denne retning, ikke? Forkert. XSLT giver ingen nem måde at erstatte forekomsten af ​​et ord eller mønster i en streng. Det samme gælder lokalisering. Det er ikke at sige, at det ikke kan gøres med standard XSLT-syntaks. Der er måder, men de er ikke nær så lette, som vi gerne vil. Hvis du virkelig vil skrive tekstmanipulationsfunktioner ved hjælp af rekursive skabeloner, vær min gæst.

XSLTs største svaghed er tekstbehandling, hvilket synes rimeligt, da formålet er at gengive XML. Men fordi XML-indhold udelukkende er tekst, har XSLT brug for stærkere teksthåndtering. Det er overflødigt at sige, at stilarkdesignere kræver en vis udvidelse fra tid til anden. Med Xalan giver Java denne udvidelighed.

Brug JDK-klasser inden for XSLT

Du kan være glad for at vide, at du ikke behøver at skrive nogen Java-kode for at drage fordel af Xalans udvidelighed. Når du bruger Xalan, kan du oprette og påkalde metoder på næsten ethvert Java-objekt. Før du bruger en Java-klasse, skal du angive en XSLT navneområde for det. Dette eksempel erklærer "java" som et navneområde for alt i eller under Java-pakken (dvs. hele JDK):

Nu har vi brug for noget at gøre. Lad os starte med et lille XML-dokument:

 Java kan være en fad J. Burke 30/30/97 

Du er blevet bedt om at style denne XML, så titlen vises med store bogstaver. En udvikler, der er ny til XSLT, ville blot åbne en XSLT-reference for at lede efter toUpper () fungere; dog ville hun være skuffet over at finde ud af, at referencen mangler en. Det Oversætte() metode er dit bedste valg, men jeg har en endnu bedre metode: java.lang.String.toUpperCase (). For at bruge denne metode skal du instantiere en Snor objekt med titelindholdet. Her er hvordan du kan oprette en ny Snor instans med titelelementets indhold:

Det navn attribut specificerer håndtaget til dit nye Snor eksempel. Du påberåber konstruktøren ved først at angive navneområdet sammen med den resterende sti til Snor klasse. Som du måske har bemærket, Snor mangler en ny() metode. Du bruger ny() at konstruere et Java-objekt i Xalan; det svarer til Java's ny nøgleord. Argumenterne givet til ny() bestemme den konstruktørversion, der skal kaldes. Nu hvor du har titelindholdet i en Java Snor objekt, kan du bruge toUpperCase () metode som sådan:

Dette ser måske underligt ud for dig i starten. Når du bruger Java-metoder på en bestemt forekomst, er det første argument den forekomst, du ønsker, at metoden skal påberåbes. Naturligvis bruger Xalan introspektion for at give denne mulighed.

Nedenfor finder du et andet trick. Her er hvordan du muligvis udsender dato og klokkeslæt hvor som helst i dit typografiark ved hjælp af java.lang. dato:

Her er noget, der gør dagen for enhver, der kræves for at lokalisere et generisk typografiark mellem to eller flere sprog. Du kan bruge java.util.ResourceBundle for at lokalisere bogstavelig tekst i et typografiark. Da din XML har et forfattertag, vil du måske udskrive "Forfatter:" ved siden af ​​personens navn.

En mulighed er at oprette et separat typografiark for hver lokalitet, dvs. en til engelsk, en anden til kinesisk osv. De problemer, der er forbundet med denne tilgang, bør være tydelige. Det er tidskrævende at holde flere stilartsversioner konsistente. Du skal også ændre din applikation, så den vælger det korrekte typografiark baseret på brugerens lokalitet.

I stedet for at duplikere typografiarket for hvert sprog kan du drage fordel af Java's lokaliseringsfunktioner. Lokalisering ved hjælp af en ResourceBundle viser en bedre tilgang. I XSLT skal du indlæse ResourceBundle i begyndelsen af ​​dine typografiark som sådan:

Det ResourceBundle klasse forventer at finde en fil kaldet Generelle ejendomme i din CLASSPATH. Når pakken er oprettet, kan den genbruges i hele stilarket. Dette eksempel henter forfatter ressource:

Læg mærke til den underlige metodesignatur. Normalt, ResourceBundle.getString () tager kun ét argument inden for XSLT skal du dog også specificere det objekt, hvormed du vil påkalde metoden.

Skriv dine egne udvidelser

I nogle sjældne situationer kan det være nødvendigt at skrive din egen XSLT-udvidelse i form af enten en udvidelsesfunktion eller et udvidelseselement. Jeg vil diskutere oprettelse af en udvidelsesfunktion, et koncept, der er ret let at forstå. Enhver Xalan-udvidelsesfunktion kan tage strenge som input og returnere strenge til XSLT-processoren. Dine udvidelser kan også tage NodeListes eller Nodes som argumenter, og returner disse typer til XSLT-processoren. Ved brug af Nodes eller NodeListes betyder, at du kan føje til det originale XML-dokument med en udvidelsesfunktion, hvilket er hvad vi vil gøre.

En type tekstelement, der ofte ses, er en dato; det giver en god mulighed for en ny XSLT-udvidelse. Vores opgave er at style et artikelelement, så datoen udskrives i følgende format:

Fredag, 30. november 200

Kan standard XSLT udfylde datoen ovenfor? XSLT kan afslutte det meste af opgaven. At bestemme den aktuelle dag er den vanskelige del. En måde til hurtigt at løse dette problem er at bruge java.text.SimpleDate formatklasse i en udvidelsesfunktion for at returnere en streng formateret, som vi ønsker. Men vent: bemærk, at dagen vises med fed skrift. Dette returnerer os til det oprindelige problem. Årsagen til, at vi endda overvejer en udvidelsesfunktion, er, at det originale XML-dokument ikke strukturerede datoen som en gruppe af noder. Hvis vores udvidelsesfunktion returnerer en streng, vil vi stadig har svært ved at style dagsfeltet anderledes end resten af ​​datostrengen. Her er et mere nyttigt format, i det mindste set fra en XSLT-designer:

  11 30 2001  

Vi opretter nu en XSLT-udvidelsesfunktion, hvor vi tager en streng som et argument og returnerer en XML-node i dette format:

  30. november fredag ​​2001 

Klassen, der er vært for vores udvidelsesfunktion, implementerer eller udvider ikke noget; vi kalder klassen DateFormatter:

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

Wow, for let, hva '? Der er absolut ingen krav til typen eller grænsefladen til en Xalan-udvidelsesfunktion. Generelt vil de fleste udvidelsesfunktioner tage en Snor som et argument og returnere en anden Snor. Andre almindelige mønstre er at sende eller modtage org.w3c.dom.NodeLists eller individuel Nodes fra en udvidelsesfunktion, som vi vil gøre. Se Xalan-dokumentationen for detaljer om, hvordan Java-typer konverteres til XSLT-typer.

I kodefragmentet ovenfor er format() metodens logik opdeles i to dele. Først skal vi analysere datostrengen fra det originale XML-dokument. Derefter bruger vi nogle DOM programmeringsteknikker til at skabe en Node og returner det til XSLT-processoren. Kroppen af ​​vores format() metodeimplementering lyder:

 Document doc = DocumentBuilderFactory.newInstance (). newDocumentBuilder (). newDocument (); Element dateNode = doc.createElement ("formateret dato"); SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance (DateFormat.SHORT, locale); df.setLenient (sand); Dato d = df. Parse (dato); df.applyPattern ("MMMM"); addChild (dateNode, "måned", df.format (d)); df.applyPattern ("EEEE"); addChild (dateNode, "ugedag", df.format (d)); df.applyPattern ("åååå"); dateNode.setAttribute ("år", df.format (d)); return dateNode; 

datoNode indeholder vores formaterede datoværdier, som vi vender tilbage til typografiarket. Bemærk, at vi har brugt java.text.SimpleDateFormat () for at analysere datoen. Dette giver os mulighed for at drage fuld fordel af Java's datostøtte, herunder dens lokaliseringsfunktioner. SimpleDateFormat håndterer den numeriske datakonvertering og returnerer måneds- og dagnavne, der svarer til lokaliteten for den VM, der kører vores applikation.

Husk: det primære formål med en udvidelsesfunktion er simpelthen at give os adgang til eksisterende Java-funktionalitet; skriv så lidt kode som muligt. En udvidelsesfunktion, som enhver Java-metode, kan bruge andre metoder inden for samme klasse. For at forenkle format() implementering flyttede jeg gentagen kode til en lille hjælpemetode:

privat ugyldigt addChild (nodeforælder, strengnavn, strengtekst) {Element child = parent.getOwnerDocument (). createElement (name); child.appendChild (parent.getOwnerDocument (). createTextNode (tekst)); parent.appendChild (barn); } 

Brug DateFormatter i et typografiark

Nu hvor vi har implementeret en udvidelsesfunktion, kan vi kalde den fra et stilark. Ligesom før er vi nødt til at erklære et navneområde til vores udvidelsesfunktion:

Denne gang kvalificerede vi stien til klassen, der er vært for udvidelsesfunktionen, fuldt ud. Dette er valgfrit og afhænger af, om du bruger andre klasser i samme pakke eller kun et enkelt udvidelsesobjekt. Du kan erklære det fulde CLASSPATH som navneområdet eller brug en pakke, og angiv den klasse, hvor udvidelsesfunktionen påberåbes. Ved at specificere det fulde CLASSPATH, vi skriver mindre, når vi kalder funktionen.

For at udnytte funktionen skal du blot kalde den indefra a Vælg tag, som sådan: