tag:blogger.com,1999:blog-7017123333978978050.post3302130172935095024..comments2022-04-01T05:15:14.655+01:00Comments on Rüdiger Plantiko: Ereignisbasierter XML-Parser in ABAPRüdiger Plantikohttp://www.blogger.com/profile/02393666282077884370noreply@blogger.comBlogger21125tag:blogger.com,1999:blog-7017123333978978050.post-384287272788119162019-11-17T21:38:18.064+01:002019-11-17T21:38:18.064+01:00Hallo Rüdiger,
Ich kann mich meinen Vorredner nur...Hallo Rüdiger,<br /><br />Ich kann mich meinen Vorredner nur anschließen, wirklich guter Post.<br /><br />Eine Frage hätte ich dazu, die von dir beschriebene Optimierung<br /><br />lo_parser->subscribe( '/DESADV/row[1]/ABSENDER::text()' ).<br /><br />führ zu einem Compilerfehler da es die Methode nicht(mehr?) gibt. Kennst du hier eine Alternative?<br /><br />Danke und Gruß <br /><br />ChristianAnonymoushttps://www.blogger.com/profile/09727120419678478120noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-1235455222092321572011-09-12T20:51:59.612+01:002011-09-12T20:51:59.612+01:00Hallo Enno,
danke fürs Aufschreiben - ein interes...Hallo Enno,<br /><br />danke fürs Aufschreiben - ein interessanter Anwendungsfall!<br /><br />Gruss,<br />RüdigerRüdiger Plantikohttps://www.blogger.com/profile/02393666282077884370noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-57104144169413877552011-09-12T14:44:52.473+01:002011-09-12T14:44:52.473+01:00SUBMIT ... EXPORTING ALV TO MEMORY
:)
Gruß
Enno<a href="http://tricktresor.de/content/index.php?navID=523&aID=625" rel="nofollow">SUBMIT ... EXPORTING ALV TO MEMORY</a><br />:)<br />Gruß<br />EnnoEnno ◄Tricktresor►https://www.blogger.com/profile/05198381030098847522noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-17037913506165477912011-09-12T11:10:43.318+01:002011-09-12T11:10:43.318+01:00ich wusste nicht, dass Du Excel-Tabellen mit mehre...<i>ich wusste nicht, dass Du Excel-Tabellen mit mehreren Millionen Datenzeilen bearbeiten musst</i><br />ich auch nicht... ;)<br />Der Einwand war wohl eher theoretischer Natur...<br />Ich zeige dir zu gegebener Zeit, wozu ich das brauchte!<br />Gruß EnnoEnno ◄Tricktresor►https://www.blogger.com/profile/05198381030098847522noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-77169597910795759822011-09-12T11:07:42.324+01:002011-09-12T11:07:42.324+01:00Hallo Enno,
ich wusste nicht, dass Du Excel-Tabe...Hallo Enno, <br /><br />ich wusste nicht, dass Du Excel-Tabellen mit mehreren Millionen Datenzeilen bearbeiten musst - warum hat Microsoft nicht die 65'535er Grenze beibehalten! :-) [Wenn es weniger als Millionen sind, dürfte meine Lösung funktionieren]<br /><br />Tabellenzeilen zu löschen, hilft wenig, da nur der belegte, aber nicht der reservierte Platz der Tabelle reduziert wird.<br /><br />Wenn Du wirklich mit Millionen von Tabellenzeilen rechnest, musst Du blockweise arbeiten. <br /><br />In meinem Beispielprogramm (immer noch http://pastebin.com/AFkjvFq1 ) wäre dann in Zeile 162 einzugreifen. Wenn die Tabelle CT_DATA mehr als N Zeilen enthält, soll ihr Inhalt durch Aufruf einer anderen Routine in das endgültige Ergebnis überführt werden, danach wird CT_DATA mit CLEAR gelöscht.<br /><br />Dadurch verhinderst Du, dass das Zwischenergebnis sich unnötig aufbläht. Denn Du hast ja schon den String selbst im Speicher (das Quell-XML), und das Ziel wird auc gross. Dann wäre es günstig, wenigstens die Zwischentabelle klein zu halten (ein paar Tausend Zeilen dürften auf einer normalen Maschine sicher immer noch gehen... ).<br /><br />Gruss,<br />RüdigerRüdiger Plantikohttps://www.blogger.com/profile/02393666282077884370noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-16315248850135319812011-09-12T10:57:38.251+01:002011-09-12T10:57:38.251+01:00Danke für dein Coding! Das ist tatsächlich eine im...Danke für dein Coding! Das ist tatsächlich eine im wahrsten Sinne des Wortes "schöne" Lösung! :)<br /><br />Ich wollte gerade schreiben, dass es einen Nachteil gibt: <br />Will man nämlich die gesammelten Daten in eine strukturierte Tabelle überführen, hätte man einen doppelten Speicherverbrauch.<br />Stimmt ja aber nicht: Ich kann ja, sobald eine Zeile abgearbeitet wurde, diese Einträge aus der Quelltabelle löschen.<br /><br />Ich probiere noch mal, ob eine "nicht-eventbasierte" Verarbeitung mittels "parse" und "direktem Zugriff" (wie nennt man das dann??) probieren. Vielleicht sieht das noch eleganter aus... ;)<br /><br />Danke!!<br /><br />Gruß EnnoEnno ◄Tricktresor►https://www.blogger.com/profile/05198381030098847522noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-3920877204324933672011-09-12T10:11:12.094+01:002011-09-12T10:11:12.094+01:00Hallo Enno,
also einfach eine Tabelle im Excel-X...Hallo Enno, <br /><br />also einfach eine Tabelle im Excel-XML-Format, OK.<br /><br />Einzige Hürde scheint doch zu sein, dass die Zahl der Spalten nicht im vorhinein bekannt ist. Dann ist eben auch das Zielformat variabel, zum Beispiel eine Tabelle von STRINGTABs. Die Properties der Spalten kannst Du im selben Parse-Prozess einlesen.<br /><br />In einem zweiten Schritt - der nicht mehr zum Thema "Parsen von XML nach ABAP gehört" !!! - wären dann die eingelesenen Daten gemäss den Microsoft-Spezifikationen für die Spaltentypen zu validieren. Zum Beispiel mit dem Baustein, der trotz seines hässlichen Namens RS_CONV_EX_2_IN_NO_DD die Grundlage der Dynpro-Feldkonvertierung String -> ABAP-Datentyp darstellt. <br /><br />Hier mein Vorschlag für das simultane Parsen der Daten und der Typeigenschaften:<br /><br />http://pastebin.com/AFkjvFq1<br /><br />Gruss,<br />RüdigerRüdiger Plantikohttps://www.blogger.com/profile/02393666282077884370noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-50905075129494815682011-09-12T08:19:00.746+01:002011-09-12T08:19:00.746+01:00http://pastebin.com/XkgctkJA
Tabelle SFLIGHT mit e...http://pastebin.com/XkgctkJA<br />Tabelle SFLIGHT mit ein paar Daten... :)<br />Mir kam es auf<br />TABLE|COLUMN<br />und dann ROW|CELL|DATA an.Enno ◄Tricktresor►https://www.blogger.com/profile/05198381030098847522noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-79883691249601149332011-09-12T08:07:28.997+01:002011-09-12T08:07:28.997+01:00Hallo Enno,
Dein Problem klingt interessant. Ich...Hallo Enno, <br /><br />Dein Problem klingt interessant. Ich mag, wie Du, auch lieber elegante als unelegante Programme. :-) <br /><br />Andererseits beherzige ich auch das amerikanische Sprichwort: "A poor craftsman who blames his tools"!<br /><br />Hättest Du Lust, ein Beispiel-XML bei pastebin zu plazieren und den Link hier zu posten? <br /><br />Gruss,<br />RüdigerRüdiger Plantikohttps://www.blogger.com/profile/02393666282077884370noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-1257676328309514472011-09-12T07:46:07.862+01:002011-09-12T07:46:07.862+01:00Hallo Rüdiger!
Hintergrund ist der folgende: Ich h...Hallo Rüdiger!<br />Hintergrund ist der folgende: Ich habe ein XML-Dokument in der Tabelleninformationen und Tabelleninhalte stehen. Die Tabelleninformationen wollte ich auslesen, um dann dynamisch eine interne Tabelle mit der entsprechenden Anzahl Felder zu generieren (1. Durchlauf).<br />Im zweiten Durchlauf wollte ich dann die Felder füllen. <br />Die Tabelleninformationen stehen natürlich VOR den eigentlichen Daten. Von daher habe ich es nun auch so gelöst, dass ich als Ereignis so lange abfrage bis das erste mal kommt und zuvor halt die interne Tabelle generiere. <br />Das finde ich jedoch programmtechnisch nicht sehr schön, da hier in einer Routine zwei völlig unterschiedliche Programmteile zusammen geworfen werden. <br />Schöner fände ich es halt, wenn ich zuerst programmieren könnte:<br />GibElement"COLUMN"<br />GibAnzahlElemente<br /><br />Um danach ereignisbasiert die Tabellendaten auszulesen.<br /><br />Es hilft leider noch nicht einmal, das PARSER-Objekt neu zu generieren.<br /><br />Gruß EnnoEnno ◄Tricktresor►https://www.blogger.com/profile/05198381030098847522noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-26681927828122625472011-09-12T07:32:20.088+01:002011-09-12T07:32:20.088+01:00Hallo Enno,
Gegenfrage: Wozu brauchst Du das?
I...Hallo Enno,<br /><br />Gegenfrage: Wozu brauchst Du das? <br /><br />Ich kann man mir gerade keinen Fall vorstellen, wo es nötig ist, ein und dasselbe XML-Dokument <i>mehrfach</i> durchzufräsen, nur um Informationen aus ihm zu extrahieren! Der Parser hält ja an jeder relevanten Stelle im Dokument, den Rest kannst Du doch in den Ereignisbehandlern hinbekommen.<br /><br />Gruss,<br />RüdigerRüdiger Plantikohttps://www.blogger.com/profile/02393666282077884370noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-19998073315902104592011-09-09T19:31:54.553+01:002011-09-09T19:31:54.553+01:00Habe grade das hier gefunden: Until now, there is ...Habe grade das hier gefunden: <a href="http://help.sap.com/SAPHELP_NWPI71/helpdata/EN/bb/576643dca511d4990b00508b6b8b11/content.htm" rel="nofollow">Until now, there is no reset() method that would allow to reset an XML parser to its initial state. Therefore a new parser instance is required for each XML document to parse.</a> :(Enno ◄Tricktresor►https://www.blogger.com/profile/05198381030098847522noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-77392513882844378282011-09-09T18:13:09.290+01:002011-09-09T18:13:09.290+01:00Hallo Rüdiger!
Ich habe versucht per "Parse_E...Hallo Rüdiger!<br />Ich habe versucht per "Parse_Event" ein XML zweimal zu durchsuchen, das funktioniert aber anscheinend nicht, da nach dem ersten Durchlauf "lr_event = lr_parser->parse_event( )." initial ist.<br /><br />Gibt es einen Befehl, um den Pointer wieder an den Anfang des Dokuments zu setzen?<br /><br />Gruß EnnoEnno ◄Tricktresor►https://www.blogger.com/profile/05198381030098847522noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-69936699107517054542010-09-28T10:26:48.435+01:002010-09-28T10:26:48.435+01:00Hallo Rüdiger
Genial! Du hast recht, es war genau...Hallo Rüdiger<br /><br />Genial! Du hast recht, es war genau der Zeilenumbruch und das fehlende behandeln des co_event_element_post Events.<br /><br />Vielen Dank und Gruss<br />flurinflurischthttps://www.blogger.com/profile/15814022291497371289noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-59023217907944333362010-09-28T09:48:39.261+01:002010-09-28T09:48:39.261+01:00Hallo Flurin,
Deine Annahme, das Text-Event würd...Hallo Flurin, <br /><br />Deine Annahme, das Text-Event würde <i>innerhalb</i> eines Elements zweimal ausgelöst, ist vermutlich falsch. Mit ziemlicher Sicherheit ist das '##' der Zeilenumbruch nach dem Abschluss des Elements (x0D0A = zwei nicht darstellbare Zeichen) und vor dem Beginn des nächsten Elements.<br /><br />Da Du Dich nur für den Text innerhalb eines Elements interessierst, musst Du das Feld mit dem aktuellen Elementnamen setzen, wenn der Beginn eines Elements entdeckt wird und dieses Feld wieder zurücksetzen, wenn das Ende des Elements (das schliessende Tag)<br /> entdeckt wird. Sowohl "öffnendes Tag entdeckt" (co_event_element_pre oder co_event_element_pre2) als auch "schliessendes Tag entdeckt" (co_event_element_post) sind Ereignisse, für die Du Dich registrieren kannst. Das Textereignis berücksichtigst Du dann nur, wenn der "aktuelle Elementname" der gesuchte ist.<br /> <br />Alternativ kannst Du aber auch einfach nach dem ersten Text-Ereignis nach Entdecken des Elementbeginns abbrechen, so wie ich es in diesem Beispiel gemacht habe. <br /><br />Hoffe, das hilft. Die <a href="http://help.sap.com/saphelp_470/helpdata/de/bb/5766a9dca511d4990b00508b6b8b11/content.htm" rel="nofollow">Doku der SAX-Parserereignisse</a> zeigt Dir weiterführende Möglichkeiten, den SAX-Parser zu verwenden.<br /><br />Gruss,<br />RüdigerRüdiger Plantikohttps://www.blogger.com/profile/02393666282077884370noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-34234975578889930882010-09-28T09:32:50.979+01:002010-09-28T09:32:50.979+01:00Hallo Rüdiger
Ich benutze deinen Ansatz zum Parse...Hallo Rüdiger<br /><br />Ich benutze deinen Ansatz zum Parsen von XML Files der folgenden Struktur:<br /><br /><?xml version="1.0" encoding="UTF-8" standalone="yes"?><br /><DOKUMENT><br /><VERTRAN>200</VERTRAN><br /><GFNUM></GFNUM><br /></DOKUMENT><br /><br />Als Events benutze ich <br />if_ixml_event=>co_event_element_pre2,<br />if_ixml_event=>co_event_text_post sowie <br />if_ixml_event=>co_event_cdata_section_post.<br /><br />Mir ist jetzt aufgefallen, dass das Event co_event_text_post pro Knoten zweimal ausgelöst wird. Im obigen Fall erhalte ich beim Knoten VERTRAN einmal 200 und einmal ## von get_value( ) zurück.<br /><br />Ist dir so etwas ähnliches auch schon aufgefallen? Mein Problem ist, dass ich nach dem ersten Event nicht abbrechen kann, da ich den kompletten Inhalt aller Knoten einlesen muss...<br /><br />Gruss flurinflurischthttps://www.blogger.com/profile/15814022291497371289noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-24105907121485708472010-09-13T22:04:25.018+01:002010-09-13T22:04:25.018+01:00Hallo Judith,
das einzige Problem, das sich beim...Hallo Judith, <br /><br />das einzige Problem, das sich beim Lesen mit fester Recordlänge stellt, ist, dass der gesuchte String "<row>" am Ende des gerade eingelesenen Records stehen kann und dann zum Teil in den folgenden Record umgebrochen wird. Du brauchst hierfür einen "1-record lookahead", d.h. Du arbeitest mit Übertrag: Du liest jeweils nicht den nächsten, sondern den übernächsten Record ein und fügst die beiden Teile zusammen (am Anfang musst Du natürlich zwei Records lesen, den nächsten und übernächsten; danach ist der nächste immer der Übertrag aus dem vorherigen Schleifendurchlauf). <br /><br />Findest Du ein oder mehrere "<row>", fügst Du alle Zeichen bis exclusive dem Beginn des letzten in den Ergebnisstring, und alles ab diesem letzten "<row>" bis Ende ist der Übertrag für den nächsten Record. Findest Du kein "<row>", so schmeisst Du den ersten Record weg, und der zweite Record wird zum Übertrag des nächsten Schleifendurchlaufs.<br /><br />Den SAX-Parser würde ich dann starten, sobald der Ergebnisstring eine ausreichende Grösse erreicht hat, z.B. 1 MB. Er durchsucht die bis dahin gefundenen <row>'s auf das Gewünschte. Wenn dann noch nicht alles Gewünschte beisammen ist, wird der gerade beschriebene Vorgang fortgesetzt - bis entweder das Gewünschte erreicht ist oder die Datei zu Ende ist.<br /><br />Ich hoffe, die Idee kommt ungefähr rüber... <br /><br />Gruss,<br />RüdigerRüdiger Plantikohttps://www.blogger.com/profile/02393666282077884370noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-62812857360270296442010-09-13T16:26:54.635+01:002010-09-13T16:26:54.635+01:00Hallo Rüdiger,
ich bin gerade auf deine Antwort ...Hallo Rüdiger,<br /><br /><br />ich bin gerade auf deine Antwort hier bzgl. Aufspalten einer XML Datei mit find und Gruppenwechsel gestoßen und hätte eine Frage dazu: Wenn ich weiß, dass mein XML Fragment mit row anfängt und /row aufhört, wie könnte ich dann beispielsweise so einen Satz finden und wegschreiben? Ich arbeite auch mit dem open und read dataset, bekomme aber keine variable Satzlänge hin :-(<br /><br />Vielleicht kannst du mir helfen? Vielen Dank...<br />JudithJayJayhttps://www.blogger.com/profile/15505678036673386390noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-896029157778524872010-09-13T16:25:50.017+01:002010-09-13T16:25:50.017+01:00Dieser Kommentar wurde vom Autor entfernt.JayJayhttps://www.blogger.com/profile/15505678036673386390noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-90014627877059297762010-07-08T20:32:15.157+01:002010-07-08T20:32:15.157+01:00Hallo Hendrik,
das ist eine gute Frage! Leider i...Hallo Hendrik, <br /><br />das ist eine gute Frage! Leider ist das Lesen der Daten-"Chunks" in den von SAP ausgelieferten Streams verborgen und nicht Teil einer öffentlichen Schnittstelle.<br /><br />Daher können wir nicht einfach eine eigene Implementierung von IF_IXML_ISTREAM programmieren, die die Chunks z.B. aus einer Datei liest, sondern sind auf die von SAP implementierten Möglichkeiten angewiesen. <br /><br />Das Interface IF_IXML_STREAM_FACTORY gibt fünf mögliche Input-Streamtypen an: CSTRING, STRING, ITABLE, XSTRING und URI. Die ersten vier erfordern ganz offensichtlich, dass der Stream-Inhalt vollständig im Memory vorliegen muss. Bei URI ist es wahrscheinlich auch so, dass die Ressource vollständig beschafft wird, selbst bei "file://" URIs. Vielleicht fragst Du mal im SDN in einem geeigneten Forum?<br /><br />Man spart mit dem in meinem Blog beschriebenen SAX-Parsing trotzdem eine Menge, denn der DOM-Parser würde ja den Eingabestream in ein vermutlich um ein Vielfaches grösseres Objekt im Speicher abbilden - das DOM-Modell. <br /><br />Bei 750 MB für das Roh-XML wird es allerdings trotzdem kritisch. Wenn Dein Prozess etwas mehr macht als nur das XML-Dokument zu lesen und zu parsen, bekommst Du vermutlich Probleme mit dem Speicher. Wenn ich eine solche Aufgabe bekäme und ich sichergestellt hätte (OSS, SDN), dass es wirlkich keinen iterativ arbeitenden ISTREAM gibt, würde ich die Aufspaltung in "Chunks" selbst vornehmen, indem ich mich stückweise durch die Datei durcharbeite (mit OPEN DATASET und READ DATASET ... MAXIMUM LENGTH ... sollte das gehen) und als Gruppenwechselkriterium ein bestimmtes Element auf Stringebene suche (also mit FIND oder SEARCH), das z.B. den nächsten Record einleitet. Sobald ich den nächsten Gruppenwechsel erreicht habe, ist das vorhergehende XML-Fragment komplett. Das Fragment kann ich dann mit dem SAX-Parser wie beschrieben näher untersuchen, z.B. bestimmte Textknoten extrahieren o.ä.<br /><br />Hoffe, das hilft Dir ein bisschen!<br />- RüdigerRüdiger Plantikohttps://www.blogger.com/profile/02393666282077884370noreply@blogger.comtag:blogger.com,1999:blog-7017123333978978050.post-69715800381086571232010-07-08T11:37:06.182+01:002010-07-08T11:37:06.182+01:00Hallo Rüdiger,
dein Beitrag hat mir sehr geholfen...Hallo Rüdiger,<br /><br />dein Beitrag hat mir sehr geholfen. Deine Erklärungen sind wirklich prima. Mir gefällt besonders, wie du aufgrund der Performance auf SAX setzt. Und genau an dieser Stelle hätte ich noch eine Frage. In deinem Beispielprogramm lässt du das Unterprogramm "get_example" offen. Ich habe für mich das Unterprogramm ausprogrammiert und erstelle den Inputstream wie folgt:<br /><br />go_stream_factory->create_istream_string(‘...’).<br /><br />In meinem konkreten Fall stehe ich vor dem Problem, eine etwa 750 MB große XML-Datei einzulesen. Ich möchte natürlich vermeiden, den gesamten Inhalt vorher in den RAM zu laden. Könntest du dir eine Möglichkeit vorstellen, iterativ (z.B. mit OPEN DATASET) zu arbeiten?<br /><br />Danke,<br />HendrikAnonymousnoreply@blogger.com