Hallo zusammen,
ich möchte in einer XML-Datei bestimmte Elemente finden.
Das XML ist wie folgt aufgebaut:
<?xml version='1.0'?>
<Locations>
<Location>
<ID1>AAA</ID1><ID2>111</ID2><Date>01.01.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>
</Location>
<Location>
<ID1>AAA</ID1><ID2>111</ID2><Date>02.02.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>
</Location>
<Location>
<ID1>AAA</ID1><ID2>222</ID2><Date>05.05.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>
</Location>
<Location>
<ID1>AAA</ID1><ID2>333</ID2><Date>03.03.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>
</Location>
<Location>
<ID1>BBB</ID1><ID2>111</ID2><Date>07.07.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>
</Location>
<Location>
<ID1>BBB</ID1><ID2>111</ID2><Date>01.01.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>
</Location>
<Location>
<ID1>BBB</ID1><ID2>222</ID2><Date>09.09.2000</Date><Status>I</Status><MoreStuffHere>...</MoreStuffHere>
</Location>
<Location>
<ID1>CCC</ID1><ID2>111</ID2><Date>10.10.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>
</Location>
<Location>
<ID1>CCC</ID1><ID2>222</ID2><Date>13.12.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>
</Location>
</Locations>
Ich möchte nun z.B. alle Location-Elemente finden, die in ID1 "AAA" stehen haben und in ID2 "111".
Wenn ich das XML über einen NotesStream in einen NotesDOMParser schubse und parse, dann kann ich ja z.B. mittels "nParser.Document.DocumentElement.GetElementsByTagName({Location})" alle Locations bekommen.
Wie komme ich aber an die Locations, die bestimmte Werte in ID1 und ID2 haben?
Iterieren ist ungünstig, da es über 120.000 Lokationen sind.
Meine Java-Kollegen schreien "XPATH!!!", aber wenn ich das richtig sehe, steht mir das in Lotus-Skript nur zur Verfügung, wenn ich über "Microsoft.XMLDOM" gehe - und das Skript muss sowohl per Agent auf einem Linux-Server laufen, als auch bei manuellem Start lokal auf Windows-Rechnern.
Auf Java-Bibliotheken oder Agenten möchte ich möglichst verzichten, die gesamte Anwendung mit LS erstellt wurde und ich im Rahmen des Abgleiches auf andere Bibliotheken zurück greifen muss.
Geht das überhaupt und wenn ja: Wie? Und falls nein: Wie würdet ihr vorgehen?
Beste Grüße,
Mitch
Hiers source code.
Um es Lotus-Script nahe zu halten hab ich ein paar Methoden geschrieben, die Java ein wenig LotusScript ähnlich macht. Sehr albern und inperformant, aber ich mach nur Spaß. Der Wahnsinn ist im 2. code Schnippsel. Aus meiner Sicht, nicht schwierig das in LotusScript zu transkribieren und ein bischen dran zu feilen.
Und hey! falls sich jemand über den Programmierstil wundert... Das ist dahingehend optimiert, dass es aus meiner Sicht für LotusScript Programmierer leicht zu lesen ist.
import static de.aja.lotus.LotusScriptEmulator.len;
import static de.aja.lotus.LotusScriptEmulator.redimPreserve;
import static de.aja.lotus.LotusScriptEmulator.ubound;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
public class LocationXmlContentHandler extends DefaultHandler {
boolean ok;
boolean insideLocation;
boolean insideData;
String data = "";
String tagName;
String newValue;
String[] treffer = new String[0];
// aufgerufen von Parser
// Für jeden startenden Tag
// In Reihenfolge, wie sie in dem XML Dokument stehen.
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
tagName = localName;
if (insideLocation) {
if (len(data) > 0) {
data = data + ":";
}
if (ok) {
data = data + localName + "=";
newValue = "";
insideData = true;
}
}
// neues location Element
if (localName.equals("Location")) {
ok = true; // ok zu Beginn des Lesens eines Datensatzes auf true
// setzen
data = "";
insideLocation = true;
}
}
// text zwischen tags
public void characters(char[] ch, int start, int length)
throws SAXException {
if (insideData && insideLocation && ok) {
newValue = newValue + new String(ch, start, length);
}
}
// Methode wird aufgerufen wenn der Parser zu einem End-Tag kommt
public void endElement(String uri, String localName, String qName)
throws SAXException {
if (insideData) {
// Regeln: wenn nicht erfüllt ok ist false und Datensatz wird nicht
// weiter verarbeitet
if (localName.equals("ID1") && (!"AAA".equals(newValue))) {
ok = false;
}
if (localName.equals("ID2") && (!"111".equals(newValue))) {
ok = false;
}
if (ok) {
data = data + newValue; // zu Datensatz, d.h. was in location
// tags steht, hinzufügen
}
}
insideData = false;
if (localName.equals("Location")) {
if (ok) {
// hinzufügen zu Array aus Datensätzen, d.h. Trefferliste.
int ubound = ubound(treffer);
treffer = redimPreserve(treffer, ubound + 1);
treffer[ubound] = data;
}
insideLocation = false;
}
}
public static void main(String[] args) {
String xmlZeugs = "<?xml version='1.0'?>"
+ "<Locations>"
+ " <Location>"
+ " <ID1>AAA</ID1><ID2>111</ID2><Date>01.01.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>"
+ " </Location>"
+ " <Location>"
+ " <ID1>AAA</ID1><ID2>111</ID2><Date>02.02.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>"
+ " </Location>"
+ " <Location>"
+ " <ID1>AAA</ID1><ID2>222</ID2><Date>05.05.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>"
+ " </Location>"
+ " <Location>"
+ " <ID1>AAA</ID1><ID2>333</ID2><Date>03.03.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>"
+ " </Location>"
+ " <Location>"
+ " <ID1>BBB</ID1><ID2>111</ID2><Date>07.07.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>"
+ " </Location>"
+ " <Location>"
+ " <ID1>BBB</ID1><ID2>111</ID2><Date>01.01.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>"
+ " </Location>"
+ " <Location>"
+ " <ID1>BBB</ID1><ID2>222</ID2><Date>09.09.2000</Date><Status>I</Status><MoreStuffHere>...</MoreStuffHere>"
+ " </Location>"
+ " <Location>"
+ " <ID1>CCC</ID1><ID2>111</ID2><Date>10.10.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>"
+ " </Location>"
+ " <Location>"
+ " <ID1>CCC</ID1><ID2>222</ID2><Date>13.12.2000</Date><Status>A</Status><MoreStuffHere>...</MoreStuffHere>"
+ " </Location>" + "</Locations>";
StringReader reader = null;
try {
// XMLReader erzeugen
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
// Pfad zur XML Datei
reader = new StringReader(xmlZeugs);
InputSource inputSource = new InputSource(reader);
// DTD kann optional übergeben werden
// inputSource.setSystemId("X:\\location.dtd");
LocationXmlContentHandler contentHandler = new LocationXmlContentHandler();
// ContentHandler wird übergeben
xmlReader.setContentHandler(contentHandler);
// Parsen wird gestartet
xmlReader.parse(inputSource);
System.out.println("treffer");
for (String hit : contentHandler.treffer) {
System.out.println(hit);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} finally {
if (reader != null) {
reader.close();
}
}
}
}
package de.aja.lotus;
public class LotusScriptEmulator {
public static String[] redimPreserve (String[] in, int ubound) {
if (in == null) throw new IllegalArgumentException("Parameter in can't be null");
if (ubound <0) throw new IllegalArgumentException("Parameter ubound should be >=0, but is" + ubound);
String [] out = new String[ubound];
for (int i=0; i < ubound;i++) {
if (in.length > i) {
out[i] = in[i];
}
}
return out;
}
public static int ubound(Object[] in) {
if (in == null) throw new IllegalArgumentException("Parameter in can't be null");
return in.length;
}
public static int len(String in) {
if (in == null) throw new IllegalArgumentException("Parameter in can't be null");
return in.length();
}
}