XML-Dokumentverarbeitung in Java mit XPath und XSLT

Die Extensible Markup Language (XML) ist derzeit sicherlich eine der heißesten Technologien. Während das Konzept der Markup-Sprachen nicht neu ist, scheint XML für Java- und Internet-Programmierer besonders attraktiv zu sein. Die Java-API für XML-Analyse (JAXP; siehe Ressourcen), die kürzlich durch den Java-Community-Prozess definiert wurde, verspricht eine gemeinsame Schnittstelle für den Zugriff auf XML-Dokumente. Das W3C hat das sogenannte Document Object Model (DOM) definiert, das eine Standardschnittstelle für die Arbeit mit einem XML-Dokument in einer Baumhierarchie bietet, während mit der Simple API for XML (SAX) ein Programm ein XML-Dokument sequentiell analysieren kann auf einem Ereignisbehandlungsmodell. Beide Standards (SAX ist ein De-facto-Standard) ergänzen den JAXP. Zusammen bieten diese drei APIs ausreichende Unterstützung für den Umgang mit XML-Dokumenten in Java.und zahlreiche Bücher auf dem Markt beschreiben ihre Verwendung.

In diesem Artikel wird eine Möglichkeit zum Umgang mit XML-Dokumenten vorgestellt, die über die Standard-Java-APIs zur Bearbeitung von XML hinausgeht. Wir werden sehen, dass XPath und XSLT in vielen Fällen einfachere und elegantere Möglichkeiten zur Lösung von Anwendungsproblemen bieten. In einigen einfachen Beispielen werden wir eine reine Java / XML-Lösung mit einer Lösung vergleichen, die XPath und / oder XSLT verwendet.

Sowohl XSLT als auch XPath sind Teil der XSL-Spezifikation (Extensible Stylesheet Language) (siehe Ressourcen). XSL besteht aus drei Teilen: der XSL-Sprachspezifikation selbst, XSL-Transformationen (XSLT) und XML Path Language (XPath). XSL ist eine Sprache zum Transformieren von XML-Dokumenten. Es enthält eine Definition - Formatieren von Objekten -, wie XML-Dokumente für die Präsentation formatiert werden können. XSLT gibt ein Vokabular zum Transformieren eines XML-Dokuments in ein anderes an. Sie können XSLT als XSL minus Formatierungsobjekte betrachten. Die XPath-Sprache adressiert bestimmte Teile von XML-Dokumenten und soll in einem XSLT-Stylesheet verwendet werden.

Für die Zwecke dieses Artikels wird davon ausgegangen, dass Sie mit den Grundlagen von XML und XSLT sowie den DOM-APIs vertraut sind. (Informationen und Tutorials zu diesen Themen finden Sie unter Ressourcen.)

Hinweis: Die Codebeispiele dieses Artikels wurden mit dem Apache Xerces XML-Parser und dem Apache Xalan XSL-Prozessor kompiliert und getestet (siehe Ressourcen).

Das Problem

In vielen Artikeln und Artikeln, die sich mit XML befassen, heißt es, dass dies das perfekte Mittel ist, um eine gute Entwurfspraxis in der Webprogrammierung zu erreichen: das Model-View-Controller-Muster (MVC) oder, vereinfacht ausgedrückt, die Trennung von Anwendungsdaten von Präsentationsdaten . Wenn die Anwendungsdaten in XML formatiert sind, können sie - normalerweise in einem Servlet oder einer Java ServerPage - einfach mithilfe eines XSL-Stylesheets an HTML-Vorlagen gebunden werden.

XML kann jedoch viel mehr als nur bei der Trennung von Modellansichten für das Frontend einer Anwendung helfen. Wir beobachten derzeit eine immer weiter verbreitete Verwendung von Komponenten (z. B. Komponenten, die unter Verwendung des EJB-Standards entwickelt wurden), die zum Zusammenstellen von Anwendungen verwendet werden können, wodurch die Entwicklerproduktivität gesteigert wird. Die Wiederverwendbarkeit von Komponenten kann verbessert werden, indem die Daten, mit denen Komponenten arbeiten, auf standardmäßige Weise formatiert werden. In der Tat können wir erwarten, dass immer mehr veröffentlichte Komponenten XML verwenden, um ihre Schnittstellen zu beschreiben.

Da XML-formatierte Daten sprachneutral sind, können sie in Fällen verwendet werden, in denen der Client eines bestimmten Anwendungsdienstes nicht bekannt ist oder in denen keine Abhängigkeiten vom Server bestehen dürfen. In B2B-Umgebungen ist es beispielsweise möglicherweise nicht akzeptabel, dass zwei Parteien für ihren Datenaustausch Abhängigkeiten von konkreten Java-Objektschnittstellen haben. Neue Technologien wie das Simple Object Access Protocol (SOAP) (siehe Ressourcen) erfüllen diese Anforderungen.

Alle diese Fälle haben eines gemeinsam: Daten werden in XML-Dokumenten gespeichert und müssen von einer Anwendung bearbeitet werden. Beispielsweise muss eine Anwendung, die verschiedene Komponenten von verschiedenen Anbietern verwendet, höchstwahrscheinlich die Struktur der (XML-) Daten ändern, um sie an die Anforderungen der Anwendung anzupassen oder einen bestimmten Standard einzuhalten.

Code, der mit den oben genannten Java-APIs geschrieben wurde, würde dies sicherlich tun. Darüber hinaus stehen immer mehr Tools zur Verfügung, mit denen Sie ein XML-Dokument in eine JavaBean umwandeln können und umgekehrt, was die Verarbeitung der Daten in einem Java-Programm erleichtert. In vielen Fällen verarbeitet die Anwendung oder zumindest ein Teil davon lediglich ein oder mehrere XML-Dokumente als Eingabe und konvertiert sie als Ausgabe in ein anderes XML-Format. Die Verwendung von Stylesheets in diesen Fällen ist eine praktikable Alternative, wie wir später in diesem Artikel sehen werden.

Verwenden Sie XPath, um Knoten in einem XML-Dokument zu suchen

Wie oben erwähnt, wird die XPath-Sprache verwendet, um bestimmte Teile eines XML-Dokuments zu lokalisieren. Als solches soll es von einem XSLT-Stylesheet verwendet werden, aber nichts hindert uns daran, es in unserem Java-Programm zu verwenden, um eine lange Iteration über eine DOM-Elementhierarchie zu vermeiden. In der Tat können wir den XSLT / XPath-Prozessor die Arbeit für uns erledigen lassen. Schauen wir uns an, wie das funktioniert.

Nehmen wir an, wir haben ein Anwendungsszenario, in dem dem Benutzer ein XML-Quelldokument präsentiert wird (möglicherweise nachdem es von einem Stylesheet verarbeitet wurde). Der Benutzer nimmt Aktualisierungen an den Daten vor und sendet zur Einsparung von Netzwerkbandbreite nur die aktualisierten Datensätze an die Anwendung zurück. Die Anwendung sucht nach dem XML-Fragment im Quelldokument, das aktualisiert werden muss, und ersetzt es durch die neuen Daten.

Wir erstellen ein kleines Beispiel, das Ihnen hilft, die verschiedenen Optionen zu verstehen. In diesem Beispiel wird davon ausgegangen, dass die Anwendung Adressdatensätze in einem behandelt addressbook. Ein Beispieldokument addressbooksieht folgendermaßen aus:

  John Smith 250 18. Ave SE Rochester MN 55902 Bill Morris 1234 Center Lane NW St. Paul MN 55123   

Die Anwendung (möglicherweise, aber nicht unbedingt ein Servlet) addressbookspeichert eine Instanz des In-Speichers als DOM- DocumentObjekt. Wenn der Benutzer eine Adresse ändert, sendet das Frontend der Anwendung nur das aktualisierte Element.

Das Element wird verwendet, um eine Adresse eindeutig zu identifizieren. es dient als Primärschlüssel. Dies wäre für eine echte Anwendung nicht sehr sinnvoll, aber wir tun dies hier, um die Dinge einfach zu halten.

Wir müssen jetzt Java-Code schreiben, mit dessen Hilfe wir das Element im Quellbaum identifizieren können, das durch das aktualisierte Element ersetzt werden muss. Die folgende findAddress()Methode zeigt, wie dies erreicht werden kann. Bitte beachten Sie, dass wir, um das Beispiel kurz zu halten, die entsprechende Fehlerbehandlung ausgelassen haben.

öffentlicher Knoten findAddress (Stringname, Dokumentquelle) {Element root = source.getDocumentElement (); NodeList nl = root.getChildNodes (); // iteriere über alle Adressknoten und finde den, der den richtigen Adressaten für hat (int i = 0; i
   
    

Der obige Code könnte höchstwahrscheinlich optimiert werden, aber es ist offensichtlich, dass das Iterieren über den DOM-Baum mühsam und fehleranfällig sein kann. Schauen wir uns nun an, wie der Zielknoten mithilfe einer einfachen XPath-Anweisung gefunden werden kann. Die Aussage könnte so aussehen:

// address [child :: addressee [text () = 'Jim Smith']] 

Wir können jetzt unsere vorherige Methode umschreiben. Dieses Mal verwenden wir die XPath-Anweisung, um den gewünschten Knoten zu finden:

public Node findAddress (String name, Document source) löst eine Ausnahme aus {// muss einige Hilfsobjekte neu erstellen XMLParserLiaison xpathSupport = new XMLParserLiaisonDefault (); XPathProcessor xpathParser = neues XPathProcessorImpl (xpathSupport); PrefixResolver prefixResolver = new PrefixResolverDefault (source.getDocumentElement ()); // Erstelle den XPath und initialisiere ihn XPath xp = new XPath (); String xpString = "// address [child :: addressee [text () = '" + name + "']]"; xpathParser.initXPath (xp, xpString, prefixResolver); // führe jetzt die XPath-Select-Anweisung aus XObject list = xp.execute (xpathSupport, source.getDocumentElement (), prefixResolver); // den resultierenden Knoten zurückgeben return list.nodeset (). item (0); }}

The above code may not look a lot better than the previous try, but most of this method's contents could be encapsulated in a helper class. The only part that changes over and over is the actual XPath expression and the target node.

This lets us create an XPathHelper class, which looks like this:

import org.w3c.dom.*; import org.xml.sax.*; import org.apache.xalan.xpath.*; import org.apache.xalan.xpath.xml.*; public class XPathHelper { XMLParserLiaison xpathSupport = null; XPathProcessor xpathParser = null; PrefixResolver prefixResolver = null; XPathHelper() { xpathSupport = new XMLParserLiaisonDefault(); xpathParser = new XPathProcessorImpl(xpathSupport); } public NodeList processXPath(String xpath, Node target) thrws SAXException { prefixResolver = new PrefixResolverDefault(target); // create the XPath and initialize it XPath xp = new XPath(); xpathParser.initXPath(xp, xpath, prefixResolver); // now execute the XPath select statement XObject list = xp.execute(xpathSupport, target, prefixResolver); // return the resulting node return list.nodeset(); } } 

After creating the helper class, we can rewrite our finder method again, which is now very short:

public Node findAddress(String name, Document source) throws Exception { XPathHelper xpathHelper = new XPathHelper(); NodeList nl = xpathHelper.processXPath( "//address[child::addressee[text() = '"+name+"']]", source.getDocumentElement()); return nl.item(0); } 

The helper class can now be used whenever a node or a set of nodes needs to be located in a given XML document. The actual XPath statement could even be loaded from an external source, so that changes could be made on the fly if the source document structure changes. In this case, no recompile is necessary.

Process XML documents with XSL stylesheets

In some cases, it makes sense to outsource the entire handling of an XML document to an external XSL stylesheet, a process in some respects similar to the use of XPath as described in the previous section. With XSL stylesheets, you can create an output document by selecting nodes from the input document and merging their content with stylesheet content, based on pattern rules.

If an application changes the structure and content of an XML document and producing a new document, it may be better and easier to use a stylesheet to handle the work rather than writing a Java program that does the same job. The stylesheet is most likely stored in an external file, allowing you to change it on the fly, without the need to recompile.

Zum Beispiel könnten wir die Verarbeitung für das addressbookBeispiel durchführen, indem wir ein Stylesheet erstellen, das die zwischengespeicherte Version von addressbookmit der aktualisierten zusammenführt und so ein neues Dokument mit den darin enthaltenen Aktualisierungen erstellt.

Hier ist ein Beispiel für ein solches Stylesheet:

   //mymachine.com/changed.xml