Entwicklung

Software development issues

GWT – java.lang.NoClassDefFoundError bei der Verwendung von .jars mit eclipse

10.12.2009 19:19:19 com.google.apphosting.utils.jetty.JettyLogger warn
WARNUNG: Nested in javax.servlet.ServletException: init:
java.lang.NoClassDefFoundError: org/jibble/pircbot/PircBot
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:675)

project_right_clickDiese Exception kann auftreten, wenn man versucht ein externes .jar mit GWT für die Serverseite zu verwenden. Die Fehlermeldung sagt aus, daß der eingebundene Pfad zu dem .jar (in diesem Fall org.jibble.pircbot.PircBot) nicht gefunden werden kann. Dies passiert normalerweise nur dann, wenn der Pfad zu dem .jar im JAVA Build Path nicht gesetzt wurde. Wenn man mit GWT arbeitet, ist das aber leider nicht alles was getan werden muß:

Der übliche Weg ein .jar einzubinden ist, die Einstellungen des Projektes zu bearbeiten, indem man mit der rechtenProject Properties Maustaste auf das Projekt klickt und aus dem PopUp-Menu “Eigenschaften” oder “Properties” wählt. In dem erscheinenden Fenster wählt man dann “Java Build Path” und ergänzt hier dann die externen .jars.

Wenn man das GWT-Plugin für eclipse verwendet, dann ist das nicht die einzige Stelle wo das .jar ergänzt werden muß. Zusätzlich muß hier das .jar noch in das WAR-Verzeichnis des Projektes kopiert werden. Um genau zu sein muß das .jar in folgendem Pfad abgelegt werden: YouProject/war/WEB-INF/lib/.

Nachdem man das .jar dort abgelegt hat, wird die lokale GWT-Runtime ohne weitere Fehlermeldung funktionieren. Der Hintergrund dazu ist folgender: Die GWT-Runtime des eclipse Plugins verwendet nicht die eclipse-Umgebung, sondern läuft ausschließlich aus dem “war”-Verzeichnis des Projektes, welches später auch auf den Application-Server deployed wird.

eclipse workbench reparieren – defekter eclipse workspace

Mein Workbench in eclipse wird leider sehr häufig beschädigt, und all die guten Ratschläge im Netz wie dies zu reparieren sei funktionieren nicht, da sie von der Tatsache ausgehen, daß eclipse gestartet ist. Was aber, wenn eclipse wegen der beschädigten Workbench überhaupt nicht mehr startet (Wie das normal bei mir der Fall ist)?

Als erstes sollte man das Logfile prüfen. Es ist im workbench-Verzeichnis zu finden und heißt “.metdata”. In diesem Verzeichnis ist die Datei “.log” zu finden. Öffne diese Datei und siehe nach, ob hier nicht etwas nützliches zu finden ist. Hier ist das File, welches ich heute erhalten habe:

!ENTRY org.eclipse.osgi 4 0 2009-06-25 18:56:21.330
!MESSAGE Application error
!STACK 1
org.apache.commons.lang.exception.NestableError: Java Model Exception: Java Model Status [src/net/project/windows [in project] does not exist]
	at com.instantiations.designer.core.utils.execution.ExecutionUtils.runRethrow(ExecutionUtils.java:66)
	at com.instantiations.designer.core.editor.DesignPage.initialize(DesignPage.java:96)
	at com.instantiations.designer.core.editor.multi.MultiMode.create(MultiMode.java:74)
	at com.instantiations.designer.core.editor.multi.MultiPagesMode.create(MultiPagesMode.java:63)
	at com.instantiations.designer.core.editor.multi.MultiPageEditor.createPartControl(MultiPageEditor.java:57)
	at org.eclipse.ui.internal.EditorReference.createPartHelper(EditorReference.java:661)
	at org.eclipse.ui.internal.EditorReference.createPart(EditorReference.java:428)
	at org.eclipse.ui.internal.WorkbenchPartReference.getPart(WorkbenchPartReference.java:594)
	at org.eclipse.ui.internal.PartPane.setVisible(PartPane.java:306)
	at org.eclipse.ui.internal.presentations.PresentablePart.setVisible(PresentablePart.java:180)
	at org.eclipse.ui.internal.presentations.util.PresentablePartFolder.select(PresentablePartFolder.java:270)
	at org.eclipse.ui.internal.presentations.util.LeftToRightTabOrder.select(LeftToRightTabOrder.java:65)
	at org.eclipse.ui.internal.presentations.util.TabbedStackPresentation.selectPart(TabbedStackPresentation.java:473)
	at org.eclipse.ui.internal.PartStack.refreshPresentationSelection(PartStack.java:1256)
	at org.eclipse.ui.internal.PartStack.handleDeferredEvents(PartStack.java:1224)
	at org.eclipse.ui.internal.LayoutPart.deferUpdates(LayoutPart.java:400)
	at org.eclipse.ui.internal.PartSashContainer.handleDeferredEvents(PartSashContainer.java:1401)
	at org.eclipse.ui.internal.LayoutPart.deferUpdates(LayoutPart.java:400)
	at org.eclipse.ui.internal.WorkbenchPage.handleDeferredEvents(WorkbenchPage.java:1383)
	at org.eclipse.ui.internal.WorkbenchPage.deferUpdates(WorkbenchPage.java:1373)
	at org.eclipse.ui.internal.WorkbenchPage.access$14(WorkbenchPage.java:1364)
	at org.eclipse.ui.internal.WorkbenchPage$15.runWithException(WorkbenchPage.java:3261)
	at org.eclipse.ui.internal.StartupThreading$StartupRunnable.run(StartupThreading.java:31)
	at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
	at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:133)
	at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3342)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3071)
	at org.eclipse.ui.application.WorkbenchAdvisor.openWindows(WorkbenchAdvisor.java:803)
	at org.eclipse.ui.internal.Workbench$27.runWithException(Workbench.java:1363)
	at org.eclipse.ui.internal.StartupThreading$StartupRunnable.run(StartupThreading.java:31)
	at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
	at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:133)
	at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3342)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3071)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2295)
	at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2200)
	at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:495)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:288)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:490)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:113)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:193)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:386)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:585)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:549)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:504)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1236)
Caused by: Java Model Exception: Java Model Status [src/net/siteduke/windows [in SiteDuke] does not exist]
	at org.eclipse.jdt.internal.core.JavaElement.newNotPresentException(JavaElement.java:491)
	at org.eclipse.jdt.internal.core.PackageFragmentRoot.getUnderlyingResource(PackageFragmentRoot.java:678)
	at org.eclipse.jdt.internal.core.PackageFragment.getUnderlyingResource(PackageFragment.java:337)
	at org.eclipse.jdt.internal.core.Openable.getUnderlyingResource(Openable.java:333)
	at org.eclipse.jdt.internal.core.CompilationUnit.getUnderlyingResource(CompilationUnit.java:916)
	at com.instantiations.designer.core.editor.UndoManager.<init>(UndoManager.java:62)
	at com.instantiations.designer.core.editor.DesignPage$2.run(DesignPage.java:99)
	at com.instantiations.designer.core.utils.execution.ExecutionUtils.runRethrow(ExecutionUtils.java:64)
	... 52 more
 
!ENTRY org.eclipse.osgi 2 0 2009-06-25 18:56:21.409
!MESSAGE The following is a complete list of bundles which are not resolved, see the prior log entry for the root cause if it exists:
!SUBENTRY 1 org.eclipse.osgi 2 0 2009-06-25 18:56:21.409
!MESSAGE Bundle reference:file:plugins/org.eclipse.jdt.apt.pluggable.core_1.0.101.R34x_v20081108-1950.jar [216] was not resolved.
!SUBENTRY 2 org.eclipse.jdt.apt.pluggable.core 2 0 2009-06-25 18:56:21.410
!MESSAGE Missing imported package org.eclipse.jdt.internal.compiler.tool_0.0.0.
!SUBENTRY 2 org.eclipse.jdt.apt.pluggable.core 2 0 2009-06-25 18:56:21.410
!MESSAGE Missing imported package org.eclipse.jdt.internal.compiler.apt.dispatch_0.0.0.
!SUBENTRY 2 org.eclipse.jdt.apt.pluggable.core 2 0 2009-06-25 18:56:21.410
!MESSAGE Missing imported package org.eclipse.jdt.internal.compiler.apt.model_0.0.0.
!SUBENTRY 2 org.eclipse.jdt.apt.pluggable.core 2 0 2009-06-25 18:56:21.411
!MESSAGE Missing imported package org.eclipse.jdt.internal.compiler.apt.util_0.0.0.
!SUBENTRY 1 org.eclipse.osgi 2 0 2009-06-25 18:56:21.411
!MESSAGE Bundle reference:file:plugins/org.eclipse.jdt.compiler.apt_1.0.101.R34x_v20090114-1205.jar [218] was not resolved.
!SUBENTRY 2 org.eclipse.jdt.compiler.apt 2 0 2009-06-25 18:56:21.412
!MESSAGE Missing imported package org.eclipse.jdt.internal.compiler.tool_0.0.0.
!SUBENTRY 1 org.eclipse.osgi 2 0 2009-06-25 18:56:21.412
!MESSAGE Bundle reference:file:plugins/org.eclipse.jdt.compiler.tool_1.0.100.v_894_R34x.jar [219] was not resolved.
!SUBENTRY 1 org.eclipse.osgi 2 0 2009-06-25 18:56:21.412
!MESSAGE Bundle reference:file:plugins/com.instantiations.designer.swing.java6_7.0.0.200905010653.jar [417] was not resolved.

In diesem speziellen Fall steckt die nützliche Information hier: org.apache.commons.lang.exception.NestableError: Java Model Exception: Java Model Status [src/net/project/windows [in project] does not exist] Aus irgendeinem Grund kann eclipse eine spezielle Datei nicht mehr auf der Workbench öffnen. Nach einigen Versuchen habe ich dann einen Weg gefunden dieses Problem zu umgehen:

  • Lege eine Sicherheitskopie des “.metadata“-Verzeichnisses an (sehr wichtig!!)
  • Navigiere in das Verzeichnis “.plugins” welches sich in “.metadata” befindet
  • Lösche dort das Verzeichnis “org.eclipse.ui.workbench
  • Starte wieder eclipse und prüfe, ob noch alle Daten vorhanden sind.

Tip: Lege so häufig wie möglich eine komplette Sicherungskopie Deiner workbench an!

Ach ja – bitte vergiss auf keinen Fall: Du folgst diesem Rat auf eigene Verantwortung ;)

Einen String in einen InputStream konvertieren

In den letzten Tagen wurde ich des häufigeren gefragt: “Wie konvertiere ich einen string in einen InputStream, sodaß ich meinen XML-String zum Beispiel mit SAX parsen kann?” (Anmerkung: oder natürlich jedem anderen XML-Parser wie DOM, JAXP, JDOM, …).

Um es nicht noch häufiger erklären zu müssen, hier ein Codebeispiel:

1
2
3
4
5
6
7
String myString = 'content of your very own string';
ByteArrayInputStream in = new ByteArrayInputStream(myString.getBytes());
InputSource is = new InputSource();
is.setByteStream(in);
 
Parser myParser = new Parser();
myParser.parse(is);

So einfach und schnell funktioniert das in Java :)

Erste Schritte mit GWT und XML unter com.google.gwt.xml

Die Entwicklung von Webapplikationen und Portalen mit eclipse, GWT (Google Web Toolkit) und apache tomcat ist einer der effektivsten Wege die es derzeit gibt: Das Ergebnis ist eine hochperformante, schnelle AJAX-Applikation. Um von der AJAX-Oberfläche mit dem tomcat-Server Daten auszutauschen, benutze ich gerne die GWT RPC, über die ich die Daten in XML-Form austausche.

Hierfür gibt es im GWT ein Paket: com.google.gwt.xml.client.*

com.google.gwt.xml.client.* ist ein vollständiges Toolkit um schnell und effektiv auf Clientseite (JavaScript) mit XML Datenströmen umzugehen.

Nachdem ich die notwendigen Pakete wie üblich importiert habe und einige Methoden zur Erstellung einer XML-Struktur geschrieben habe, trat beim Start der Applikation der folgende Fehler auf:

Line 52: No source code is available for type
com.google.gwt.xml.client.Document; did you forget to inherit a
required module?

Bis zu diesem Punkt war ich gewohnt, daß eclipse alles für mich regelt; so hatte ich keinen Ansatzpunkt, warum dieses Modul nicht korrekt vererbt wurde. So machte ich mich auf eine lange, aber erfolgreiche Suche in der Dokumentation und dem Projekt selbst, da auch google zu diesem Fehler keine Ergebnisse lieferte:

Um das XML-Modul korrekt zu vererben muß in der “gwt.xml” folgende Zeile ergänzt werden:

<inherits name="com.google.gwt.xml.XML" />

Die “gwt.xml” Datei kann unter folgendem Pfad gefunden werden: <project>/src/<your domain>/<project>.gwt.xml

Nachdem ich diese Zeile ergänzt hatte, ist es sehr einfach mit XML-Strukturen umzugehen. Ich werde hierzu noch einen Artikel veröffentlichen, sowie ich wieder etwas mehr Zeit für meinen Blog habe.

Bestes XML Modul für perl? XML::LibXML im Vergleich zu XML::Mini::Document

Mein letzter Artikel beschäftigte sich mit XML::Simple, einem (wie der Name schon sagt) simplen und einfach zu verstehendem Modul für Perl um XML-Strukturen zu parsen. Heute wollen wir uns mit einem weiteren Modul beschäftigen: XML::Mini::Document.

Kurzinformation:
3 von 5 Sternen Rating auf cpan
Letzte Änderung: 05 Feb. 2008

XML::Mini::Document ist ein Modul, welches auf Anhieb eher den Eindruck eines mächtigen XML-Parsers macht, da es nicht nur über die Option verfügt XML-Strukturen in ein Hash zu parsen, bzw. daraus zu generieren. Es verfügt auch über eine API, mit der man die XML-Strukturen selbst beeinflussen kann. Die Dokumentation und die Beispiele sind einfach und verständlich gehalten, sodaß man sich schnell mit dem Modul zurechtfindet.

Zunächst verwenden wir das selbe test-xml, welches wir auch schon für den Test von XML::Simple verwendet haben:

<?xml version="1.0" encoding="iso-8859-1"?>
<test debug="0" attr1="1" attr2="2" another="&lt;&gt;">
	<info attr1="perl" attr2="xml module" />
	<info attr1="perl" attr2="xml module" />
	<info attr1="perl" attr2="xml module" />
	<info attr1="perl" attr2="xml module"><deepinfo>last text here</deepinfo></info>
</test>

Und wie wollen die selben Operationen im XML ausführen, wie wir das mit XML::Simple davor ebenfalls getan haben. Wenn wir die “toHash()” Methode verwenden, so sieht der Quellcode nur leicht anders aus; das Testprogramm sieht dann wie folgt aus:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/usr/bin/perl
# Simple XML module test unit
###
# We are strict, cauz we are Elitecoderz!
use strict;
use XML::Mini::Document;
use XML::Mini;
 
################
# Get / Check Parameter (here we get the xml file we wanna deal with)
if ($#ARGV+1 != 1) {
	print "Error: Wrong number of parameters.\n";
	exit(1);
}
my $input = $ARGV[0];
chomp($input);
 
################
# Read Inputfile / check content / validate XML structure
# Some modules are able to read directly from a file; for easy going, we use this method here.
 
# Direct, dirty, but simple reading of a file
open(FILE, "<$input") || die "Error: File not readable.\n";
my @lines = <FILE>;
close(FILE);
 
# Put the lines into one string for this parser
my $XMLString = join(' ',@lines);
 
 
####################################################################
# XML::Mini
my $xmlDoc = XML::Mini::Document->new();
eval {
	$xmlDoc->parse($XMLString);
};
if ($@) {
	print "Error: XML parsing error: $@\n";
	exit(1);
}
 
my $xmlHash = $xmlDoc->toHash();
 
# Adding an attribute and a text to the first node
$xmlHash->{'test'}->{'info'}->[0]->{'addon'} = 'valid text';
$xmlHash->{'test'}->{'info'}->[0]->{'content'} = "Here is an valid xml text\nusing linebreaks";
 
# Adding an attribute and an unescaped text to the second node
$xmlHash->{'test'}->{'info'}->[1]->{'addon'} = 'invalid unescaped text';
$xmlHash->{'test'}->{'info'}->[1]->{'content'} = "Here is an valid xml text\nusing linebreaks\nand unescaped characters like < and >\ndo you see the and?";
 
# Adding a third node unescaped text to the second node
$xmlHash->{'test'}->{'info'}->[2]->{'addon'} = 'valid unescaped text in cdata';
$xmlHash->{'test'}->{'info'}->[2]->{'content'} = "<![CDATA[Here is an valid xml text\nusing linebreaks\nand unescaped characters like < and >\ndo you see the and?]]>";
 
# my $newDoc = XML::Mini::Document->new();
$xmlDoc->fromHash($xmlHash);
open(DATEI, ">output_XMLMini") || die "Datei nicht gefunden";
print DATEI $xmlDoc->toString();
close(DATEI);

Das resultierende XML sieht wie folgt aus:

<test>
	<info>
		<attr2>
			xml module
		</attr2>
		<addon>
			valid text
		</addon>
		<attr1>
			perl
		</attr1>
		<content>
			Here is an valid xml text using linebreaks
		</content>
	</info>
	<info>
		<attr2>
			xml module
		</attr2>
		<addon>
			invalid unescaped text
		</addon>
		<attr1>
			perl
		</attr1>
		<content>
			Here is an valid xml text using linebreaks and unescaped characters like &lt; and &gt; do you see the and?
		</content>
	</info>
	<info>
		<attr2>
			xml module
		</attr2>
		<addon>
			valid unescaped text in cdata
		</addon>
		<attr1>
			perl
		</attr1>
		<content>
			&lt;![CDATA[Here is an valid xml text using linebreaks and unescaped characters like &lt; and &gt; do you see the and?]]&gt;
		</content>
	</info>
	<info>
		<attr2>
			xml module
		</attr2>
		<attr1>
			perl
		</attr1>
		<deepinfo>
			last text here
		</deepinfo>
	</info>
	<attr2>
		2
	</attr2>
	<attr1>
		1
	</attr1>
	<another>
		&amp;lt;&amp;gt;
	</another>
	<debug>
		0
	</debug>
</test>
<xml>
	<version>
		1.0
	</version>
	<encoding>
		iso-8859-1
	</encoding>
</xml>

*AUTSCH* – was zum Teufel ist mit unserem XML passiert?! Nach dem ersten Schock kann man erkennen, daß alle Attribute in XML-Knoten verwandelt wurden. Das ist zwar ein gültiger XML-Syntax, und alle Informationen sind nach wie vor enthalten, aber das Problem ist, daß jemand anderes ein erwartetes Attribut vermutlich nicht als XML-Knoten suchen wird. Das Gute dieses Mal ist jedoch, daß der “deepinfo”-Knoten in der Hirachie richtig steht.

Ein anderes Problem ist der Umgang mit CDATAs. XML::Mini::Document escaped den CDATA-Knoten und ist offensichtlich nicht in der Lage ein CDATA korrekt zu schreiben (jedenfalls habe ich keine Möglichkeit gefunden).

Das übelste Problem jedoch ist die Behandlung des “XML-Informations”-Knotens; Er wird als XML-Knoten behandelt, aufgebrochen und am Ende der Datei eingefügt!

Erstes Ergebnis:

Die Methoden “fromHash()” and “toHash()” in XML::Mini::Document sind technischer Müll, die bestenfalls in XMLs ohne Attribute und CDATAs zum Einsatz kommen können.

Aber es gibt einen weiteren Weg XML::Mini::Document zu nutzen

Im Gegensatz zu XML::Simple ist bei XML::Mini::Document das “Hash-Verfahren” nicht die einzige Option wie mit XML-Strukturen umgegangen werden kann. Es kann mit der Struktur über eine Toolbox auch direkt gearbeitet werden. Hierfür schreiben wir unser Testprogramm etwas um:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/perl
# Simple XML module test unit
###
# We are strict, cauz we are Elitecoderz!
use strict;
use XML::Mini::Document;
use XML::Mini;
 
################
# Get / Check Parameter (here we get the xml file we wanna deal with)
if ($#ARGV+1 != 1) {
	print "Error: Wrong number of parameters.\n";
	exit(1);
}
my $input = $ARGV[0];
chomp($input);
 
################
# Read Inputfile / check content / validate XML structure
# Some modules are able to read directly from a file; for easy going, we use this method here.
 
# Direct, dirty, but simple reading of a file
open(FILE, "<$input") || die "Error: File not readable.\n";
my @lines = <FILE>;
close(FILE);
 
# Put the lines into one string for this parser
my $XMLString = join(' ',@lines);
 
####################################################################
# XML::Mini
my $xmlDoc = XML::Mini::Document->new();
eval {
	$xmlDoc->parse($XMLString);
};
if ($@) {
	print "Error: XML parsing error: $@\n";
	exit(1);
}
my $xmlRoot = $xmlDoc->getRoot();
 
my $firstnode = $xmlDoc->getElementByPath('test/info');
$firstnode->attribute('addon', "Here is an valid xml text\nusing linebreaks");
 
my $secondnode = $xmlDoc->getElementByPath('test/info',1,2);
$secondnode->attribute('addon','invalid unescaped text');
$secondnode->text("Here is an valid xml text\nusing linebreaks\nand unescaped characters like < and >\ndo you see the and?");
 
my $testnode = $xmlDoc->getElementByPath('test');
my $newchild = $testnode->createChild("info");
$newchild->attribute('addon', "unescaped text for a CDATA");
$newchild->cdata("Here is an valid xml text\nusing linebreaks\nand unescaped characters like < and >\ndo you see the and?");
 
open(DATEI, ">output_XMLMini3") || die "Datei nicht gefunden";
print DATEI $xmlDoc->toString();
close(DATEI);

Das Ergebnis sieht hier wie folgt aus:

<?xml version="1.0" encoding="iso-8859-1"?>
<test another="&lt;&gt;" attr1="1" attr2="2" debug="0">
	<info addon="Here is an valid xml text
using linebreaks" attr1="perl" attr2="xml module" />
	<info addon="invalid unescaped text" attr1="perl" attr2="xml module">
		Here is an valid xml text using linebreaks and unescaped characters like &lt; and &gt; do you see the and?
	</info>
	<info attr1="perl" attr2="xml module" />
	<info attr1="perl" attr2="xml
                        module">
		<deepinfo>
			last text here
		</deepinfo>
	</info>
	<info addon="unescaped text for a CDATA">
<![CDATA[ Here is an valid xml text
using linebreaks
and unescaped characters like < and >
do you see the and? ]]> 
	</info>
</test>

*Wow* – nach dem schlechten Bild zum Start macht dieses Ergbnis einen sehr guten Eindruck! Die Struktur ist komplett, ist inhaltlich korrekt und wurde korrekt escaped. In kurzen Worten: Das XML ist perfekt wie erwartet! Es scheint sich also zu lohnen einen weiteren Blick auf XML::Mini::Document zu werfen. Und genau das werden wir im nächsten Kapitel tun.

Was passiert beim parsing von aufwendigeren XML-Strukturen, die CDATAs und escaped Texte enthalten?

Um das testen zu können, passen wir unser Testprogramm nocheinmal etwas an

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/perl
# Simple XML module test unit
###
# We are strict, cauz we are Elitecoderz!
use strict;
use XML::Mini::Document;
use XML::Mini;
 
################
# Get / Check Parameter (here we get the xml file we wanna deal with)
if ($#ARGV+1 != 1) {
	print "Error: Wrong number of parameters.\n";
	exit(1);
}
my $input = $ARGV[0];
chomp($input);
 
################
# Read Inputfile / check content / validate XML structure
# Some modules are able to read directly from a file; for easy going, we use this method here.
 
# Direct, dirty, but simple reading of a file
open(FILE, "<$input") || die "Error: File not readable.\n";
my @lines = <FILE>;
close(FILE);
 
# Put the lines into one string for this parser
my $XMLString = join(' ',@lines);
 
####################################################################
# XML::Mini
my $xmlDoc = XML::Mini::Document->new();
eval {
	$xmlDoc->parse($XMLString);
};
if ($@) {
	print "Error: XML parsing error: $@\n";
	exit(1);
}
my $xmlRoot = $xmlDoc->getRoot();
 
my $firstnode = $xmlDoc->getElementByPath('test/info');
$firstnode->attribute('addon', "Here is an valid xml text\nusing linebreaks");
 
my $cdatanode = $xmlDoc->getElementByPath('test/info',1,5);
print "\n\n--\n".$cdatanode->getValue."\n--\n\n";
 
my $textnode = $xmlDoc->getElementByPath('test/info',1,2);
print "\n\n--\n".$textnode->getValue."\n--\n\n";
 
open(DATEI, ">output_XMLMini4") || die "Datei nicht gefunden";
print DATEI $xmlDoc->toString();
close(DATEI);

und nehmen als Test-XML das eben selbst generierte XML-File her. Die Ausgabe die wir erhalten sieht wie folgt aus:

--
 Here is an valid xml text
 using linebreaks
 and unescaped characters like < and > do you see the and?
--

--
Here is an valid xml text
 using linebreaks
 and unescaped characters like &lt; and &gt;
 do you see the and?
--

Das Ergebnis des CDATAs sieht perfekt aus, der aus dem zweiten Knoten ausgelesene Text wurde jedoch leider nicht unescaped. Es ist zwar kein große Sache dies selbst zu tun, aber meiner Meinung nach sollte das XML::Mini::Document für mich tun.

Ergebnis:
Die Methode “fromHash()” sollte man tunlichst nicht verwenden, es sei denn man ist darauf aus seine XML-Struktur gründlich zu zerstören. Die “toHash()” Methode kann eventuell ganz nützlich sein um kurz und schnell Informationen aus einem XML auszulesen.

Die Toolbox für das direkte bearbeiten von XML-Strukturen hingegen ist sehr praktisch. Besonders gut gefällt mir die Idee und der Syntax um in bestimmten Pfaden zu navigieren (durch anhängen des “node-count”). Geniale Idee – hervorragende Umsetzung!

XML::Mini::Document kann mit allen XML-Strukturen umgehen und escaped Inhalte -wo benötigt- automatisch. Das Unescaping von Texten muß dagegen leider händisch vorgenommen werden – in meinen Augen ein klarer Bug der mich davon abhält dieses Modul produktiv zu nutzen.

XML::Mini::Document ist klar die Beste Wahl, wenn man mit etwas komplexeren XML-Strukturen umgehen muß. Da das unescaping “händisch” selbst gemacht werden muß empfehle ich jedoch dieses Modul nicht bei großen, komplexen und “empfindlichen” XMLs einzusetzen. Ansonsten ist XML::Mini::Document ein einfacher und schneller Weg mit XMLs zu arbeiten.

Im nächsten Artikel werde ich mich mit dem Thema “Kompfortabler Umgang mit XML::LibXML” beschäftigen.
Alle oben durchgeführten Tests hat die XML::LibXML ohne Schwächen oder Fehler bestanden. Dafür gibt es einige andere Probleme, die einem das Leben unter XML::LibXML schwer machen.

Get in contact:

Kategorien