Autor Thema: XML & LotusScript: Nach Pfad/Element mit bestimmten Inhalt suchen  (Gelesen 11596 mal)

Mitch

  • Gast
Hallo zusammen,

ich möchte in einer XML-Datei bestimmte Elemente finden.

Das XML ist wie folgt aufgebaut:

Code
<?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
« Letzte Änderung: 17.09.12 - 09:43:32 von Mitch »

Offline pram

  • Gold Platin u.s.w. member:)
  • *****
  • Beiträge: 1.170
  • Geschlecht: Männlich
    • Foconis Object Framework
Du kannst den XSLT-Transformer dazu verwenden um dein Quell-XML mittels einem XSL-Stylesheet (in dem man XPATH verwenden kann) in ein gefiltertes XML überzuführen.

Dieses (sehr viel kleinere XML) kannst du dann mit dem Sax/Domparser parsen.

Wenn du den Parser als Output vom Transformer angibst, dann geht es sogar ohne das "Zwischen"-XML

Gruß
Roland
Roland Praml

IBM Certified Application Developer - Lotus Notes and Domino 8
Ich verwende das Foconis Object Framework

Offline flaite

  • Gold Platin u.s.w. member:)
  • *****
  • Beiträge: 2.966
    • mein del.icio.us
Zum Auslesen von Daten halte ich XPath in der Tat für einen sehr guten Weg. Wenn man das nicht hat, könntest Du dich auch in SAX reindenken. 
Das Problem bei DOM ist ja, dass die Repräsentation des gesamten XML Dokuments auf einmal in den Speicher gelesen wird.
In welcher Form möchtest Du die Location Elemente haben? Als xml oder einfach die Daten?

Mit Zwischen-XML wär ich vorsichtig. Nach meiner Meinung erhöht das eher die Komplexität.
1. Schritt: Generierung eines für DOM einfacher handhabbaren XML-Dokuments aus dem Input-XML
2. Schritt: Auslesen der Daten aus dem generierten XML-Dokument mittels DOM.
2 Schritte sind mehr als 1. Und wenn sich im Laufe der Zeit das Input-XML ein wenig ändert, dass das XSLT nicht mehr passend ist, halt ichs für sehr wahrscheinlich, dass der maintainer des Codes keinen schönen Tag hat.   
 
Ich werd heute das mal mit SAX beispielhaft programmieren. Allerdings in Java. Du solltest das aber in LotusScript transformieren können. Es gibt Beispiele für Sax mit LotusScript im Netz. Hier etwa http://flylib.com/books/en/1.590.1.66/1/ . Such nach dem Text "The next example illustrates how to parse XML text using a SAX parser".
 
Ich stimm nicht mit allen überein, aber mit vielen und sowieso unterhaltsam -> https://www.youtube.com/channel/UCr9qCdqXLm2SU0BIs6d_68Q

---

Aquí no se respeta ni la ley de la selva.
(Hier respektiert man nicht einmal das Gesetz des Dschungels)

Nicanor Parra, San Fabian, Región del Bio Bio, República de Chile

Offline flaite

  • Gold Platin u.s.w. member:)
  • *****
  • Beiträge: 2.966
    • mein del.icio.us
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. 

Code
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();
			}
		}
	}

}


Code
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();
	}
}

Ich stimm nicht mit allen überein, aber mit vielen und sowieso unterhaltsam -> https://www.youtube.com/channel/UCr9qCdqXLm2SU0BIs6d_68Q

---

Aquí no se respeta ni la ley de la selva.
(Hier respektiert man nicht einmal das Gesetz des Dschungels)

Nicanor Parra, San Fabian, Región del Bio Bio, República de Chile

Offline flaite

  • Gold Platin u.s.w. member:)
  • *****
  • Beiträge: 2.966
    • mein del.icio.us
Ein SaxParser - und den gibts in LotusScript wie den DOMParser - streamt das Dokument einmal durch, d.h. es ist gut geeignet für große Dokumente, weil nicht die ganze Dokument-XML-Repräsentation auf EINMAL in den Speicher geknallt wird.

Beim Parser meldet man einen Content-Handler an. In diesem Content-Handler kannst Du Methoden überschreiben, die aufgerufen werden, sobald der Parser auf verschiedene Stellen im xml-Dokument trifft. Etwa ein startTag (sowas wie <tag>), ein endTag (sowas wie </tag>) oder die characters zwischen den tags. Und in diesen "Call Back - Methoden" kann man dann die Werte aus dem xml rauspuzzeln und irgendwo hinschreiben.

Vorsicht: Solltest Du das in Eclipse mit Java ausprobieren muss auch die 2. Klasse im Projekt sein. Von da werden aber nur so Klassiker wie ubound, redimPreserve und Len für Java implementiert. S. die static imports. Muss man nicht ernst nehmen. Is mein persönlicher Wahnsinn.
Ich stimm nicht mit allen überein, aber mit vielen und sowieso unterhaltsam -> https://www.youtube.com/channel/UCr9qCdqXLm2SU0BIs6d_68Q

---

Aquí no se respeta ni la ley de la selva.
(Hier respektiert man nicht einmal das Gesetz des Dschungels)

Nicanor Parra, San Fabian, Región del Bio Bio, República de Chile

Mitch

  • Gast
Vielen Dank Roland und Pitiyankee,

auch wenn es mir echt richtig leid tut, Pitiyankees Code nicht zu nutzen, werde ich den Weg über das Stylesheet gehen.

Mit dem Sax-Parser habe ich schon einmal gearbeitet, und das ist nicht mein Ding. Ich komme mit der Ablauf-Logik nicht zurecht, das springt zu sehr hin und her. Wenn ich da nach einiger Zeit nochmal ran muss, dann stehe ich erstmal wieder doof da.

Gruß,

Mitch

Offline flaite

  • Gold Platin u.s.w. member:)
  • *****
  • Beiträge: 2.966
    • mein del.icio.us

auch wenn es mir echt richtig leid tut, Pitiyankees Code nicht zu nutzen, werde ich den Weg über das Stylesheet gehen.

Mit-Leid wird akzeptiert, wenn Du als Entschädigung das xslt postest.  ;D

Lotus Script hat da eine Möglichkeit xslt-prozessoren und parser zu chainen. Steht afaik in der Hilfe.
Ich stimm nicht mit allen überein, aber mit vielen und sowieso unterhaltsam -> https://www.youtube.com/channel/UCr9qCdqXLm2SU0BIs6d_68Q

---

Aquí no se respeta ni la ley de la selva.
(Hier respektiert man nicht einmal das Gesetz des Dschungels)

Nicanor Parra, San Fabian, Región del Bio Bio, República de Chile

 

Impressum Atnotes.de  -  Powered by Syslords Solutions  -  Datenschutz