#!c:\Perl\bin\perl.exe #!/usr/bin/perl # fmp2perl.pm package FMPro::fmp2perl; use strict; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); require Exporter; @ISA = qw(Exporter AutoLoader); @EXPORT = qw(); @EXPORT_OK = qw(fmp2perl); $VERSION = '1.1.03'; ################################################################## # fmp2perl # # Parst FMPro XML Ausgabe; verarbeitet nur UTF-8 XML # Es wird Konvertierung nach latin1 vorgenommen # # input: $String oder # "http://www.domain.de/FMPro?-DB=Datenbank&-format=-dso_xml&-Find=" oder # "http://www.domain.de/FMPro?-DB=Datenbank&-format=-fmp_xml&-Find=" oder # "c:\\Inetpub\\wwwroot\\Baurat\\tmp\\Datei.xml" # # output:Referenz $xml oder "Error - Errortext" # z.B. # $xml->[3]{Kurzbezeichnung}{Data}[0] ==> Kurzbezeichnung des 3. Treffer-DS # ################################################################## sub fmp2perl { use LWP::Simple; # get, getstore my ($input) = @_; my ($tmp,$i,$j,$String,$xml,$switch); BEGIN { unless (eval "use Unicode::String qw(latin1 utf8)") { $switch = "noUnicode"; } else { Unicode::String->stringify_as('utf8'); $switch = "okUnicode"; } } ################################################################## ### Prfen, ob $input Datei ist. Wenn ja auslesen. if (-f $input) { $String = &GET_DATEI_STRING($input); unless ($String =~ m,encoding="UTF-8",im) { return("Error - Die angegebene Datei $input liegt nicht im Format UTF-8 vor."); } unless($String =~ m,<\?xml version="[\d\.]*?" encoding=".*?"\?><(FMP[DX][SM][OL]RESULT) xmlns="http://www.filemaker.com/fmp[dx][sm][ol]result">,m && $String =~ m,,m) { return("Error - Die angegebene Datei $input liegt nicht im FileMaker XML-Format (-dso_xml oder -fmp_xml) vor."); } if($1 eq "FMPDSORESULT") { $xml->[0]{CurrentFormat} = "-dso_xml"; } else { $xml->[0]{CurrentFormat} = "-fmp_xml"; } } ### Prfen, ob $input URL ist. Wenn ja anfordern. # z.B. http://domain.de?-db=BauBuch.fp5&-lay=WWW_BR_NavRe&-Format=-dso_xml&-Skip=10&-Find= elsif ($input =~ m,^http://,) { unless ($input =~ m,-format=-dso_xml,im | $input =~ m,-format=-fmp_xml,im) { return("Error - Die angegebene URL liefert kein dso_xml oder fmp_xml zurück. Bitte \"-format=-dso_xml\" angeben!"); } $String = get("$input"); # ber String kann man den ordnungsgemen Ablauf kontrollieren unless ($String =~ m,encoding="UTF-8",im) { return("Error - Das unter $input zurückgelieferte XML liegt nicht im Format UTF-8 vor."); } $xml->[0]{URL} = $input; $input =~ m,&-format=(-[fd][ms][po]_xml)&,im; $xml->[0]{CurrentFormat} = $1; if($input =~ m,&-skip=(\d*?)&,im){$xml->[0]{CurrentSkip} = $1;}else{$xml->[0]{CurrentSkip} = 0} if($input =~ m,&-max=(\d*?)&,im){$xml->[0]{CurrentMax} = $1;}else{$xml->[0]{CurrentMax} = 25;} if($input =~ m,&-lop=(\w*?)&,im){$xml->[0]{CurrentLOP} = $1;}else{$xml->[0]{CurrentLOP} = "and"} while($input =~ m,&,gm){$tmp = pos($input);} $xml->[0]{CurrentAction} = substr($input,$tmp); # Sortierfolge z.B.: &-SortField=News_Dat&-SortOrder=Descend&-SortField=News_Zei&-SortOrder=Descend& if($input =~ m,&(-sortfield=[\w\W\s\S\d\D]*&-sortorder=[ascend|descend|ascending|descending|custom=][\w\W\s\S\d\D]*?)&,im){$xml->[0]{CurrentSort} = $1;}else{$xml->[0]{CurrentSort} = ""} } ### Wenn FMPro DSO_XML/FMP_XML $input selbst parsen elsif ($input =~ m,<\?xml version="[\d\.]*?" encoding=".*?"\?><(FMP[DX][SM][OL]RESULT) xmlns="http://www.filemaker.com/fmp[dx][sm][ol]result">,m && $input =~ m,,m) { $String = $input; unless ($String =~ m,encoding="UTF-8",im) { return("Error - Das übergebene XML liegt nicht im Format UTF-8 vor."); } if($1 eq "FMPDSORESULT") { $xml->[0]{CurrentFormat} = "-dso_xml"; } else { $xml->[0]{CurrentFormat} = "-fmp_xml"; } } ### Fehlermeldung else { return("Error - Der übergebene String ist weder eine Pfadangabe, noch eine URL oder FMPro DSO_XML."); } # Konvertierung von UTF-8 nach Latin1 if($switch eq "okUnicode") { $String = new Unicode::String($String); $String = $String->latin1(); } else { $String = UTF8_LATIN1($String); } $String =~ s/\n/ /mg; ### Parsing FMPDSORESULT if ($xml->[0]{CurrentFormat} eq "-dso_xml") { my (@tmp,@tmp1,@tmp2,$k); # Werte auslesen, inclusive # CurrentRecID, CurrentModID, CurrentLayout, CurrentDatabase, CurrentError if($String =~ m,(\d*?),mg){$xml->[0]{CurrentError} = $1;} if($String =~ m,([\w\W\s\S\d\D]*?),mg){$xml->[0]{CurrentDatabase} = $1;} if($String =~ m,([\w\W\s\S\d\D]*?),mg){$xml->[0]{CurrentLayout} = $1;} @tmp = $String =~ m,([\w\W\s\S\d\D]*?),mg; $xml->[0]{CurrentOutCount} = (scalar @tmp)/3; for ($i=0; $i<@tmp; $i+=3) { $xml->[$i/3]{CurrentModID} = $tmp[$i]; $xml->[$i/3]{CurrentRecID} = $tmp[$i+1]; $xml->[$i/3]{CurrentRecordNumber} = $i/3+1; @tmp1 = $tmp[$i+2] =~ m,<(?:DATA){0}([\w\W\s\S\d\D]*?)>([\w\W\s\S\d\D]*?),mg; # \1 ist wie $1, kann aber schon wrend des Suchvorganges benutzt werden for ($j=0; $j<@tmp1; $j+=2) { if(@tmp2 = $tmp1[$j+1] =~ m,([\w\W\s\S\d\D]*?),mg) { # Wiederholfeld for($k=0;$k<@tmp2;$k++) { $xml->[$i/3]{$tmp1[$j]}{Data}[$k] = $tmp2[$k]; } } else { $xml->[$i/3]{$tmp1[$j]}{Data}[0] = $tmp1[$j+1]; } } } } ### Parsing FMPXMLRESULT oder FMPXMLLAYOUT elsif ($xml->[0]{CurrentFormat} eq "-fmp_xml") { my ($tmp,@tmp,@tmp1,@tmp2,$i,$j,$k,$m,@FeldName); # 0 if($String =~ m,(\d*?),mg) { $xml->[0]{CurrentError} = $1; } # if($String =~ m,,m) { $xml->[0]{ProductBuild} = $1; $xml->[0]{ProductName} = $2; $xml->[0]{ProductVersion} = $3; } ### Parsing FMPXMLRESULT # if($String =~ m,<\?xml version="[\d\.]*?" encoding=".*?"\?>,m && $String =~ m,,m) { if($String =~ m,,mg) { $xml->[0]{DateFormat} = $1; $xml->[0]{CurrentLayout} = $2; $xml->[0]{CurrentDatabase} = $3; $xml->[0]{CurrentRecordCount} = $4; $xml->[0]{TimeFormat} = $5; } if($String =~ m,([\w\W\s\S\d\D]*?),m) { # $tmp[0]=EmptyOk $tmp[1]=MaxReapeat $tmp[2]=Name $tmp[3]=Type $tmp[4]=EmptyOk ... @tmp = $1 =~ m,,mg; for ($i=0; $i<@tmp; $i+=4) { $xml->[0]{$tmp[$i+2]}{EmptyOk} = $tmp[$i]; $xml->[0]{$tmp[$i+2]}{MaxReapeat} = $tmp[$i+1]; $xml->[0]{$tmp[$i+2]}{Type} = $tmp[$i+3]; $FeldName[$i/4] = $tmp[$i+2]; # Feldnamen fr nchste Schleife } } if($String =~ m,([\w\W\s\S\d\D]*?),m) { $xml->[0]{CurrentFoundCount} = $1; @tmp = $2 =~ m,([\w\W\s\S\d\D]*?),mg; $xml->[0]{CurrentOutCount} = (scalar @tmp)/3; for ($j=0; $j<@tmp; $j+=3) { $xml->[$j/3]{CurrentModID} = $tmp[$j]; $xml->[$j/3]{CurrentRecID} = $tmp[$j+1]; $xml->[$j/3]{CurrentRecordNumber} = $j/3+1; if(@tmp1 = $tmp[$j+2] =~ m,([\w\W\s\S\d\D]*?),mg) # Kein Wiederholfeld { for($k=0;$k<@tmp1;$k++) { if($tmp1[$k] =~ m,,m) { # Wiederholfeld mit mindestens 2 Wiederholungen @tmp2 = split(/<\/DATA>/,$tmp1[$k]); for($m=0;$m<@tmp2;$m++) { $xml->[$j/3]{$FeldName[$k]}{Data}[$m] = $tmp2[$m]; } } else { $xml->[$j/3]{$FeldName[$k]}{Data}[0] = $tmp1[$k]; } } } } } } ### Parsing FMPXMLLAYOUT elsif($String =~ m,<\?xml version="[\d\.]*?" encoding=".*?"\?>,m && $String =~ m,,m) { if($String =~ m,,mg) { $xml->[0]{CurrentDatabase} = $1; $xml->[0]{CurrentLayout} = $2; } # 1024x1536.. @tmp1 = $String =~ m,([\w\W\s\S\d\D]*?),mg; for ($i=0; $i<@tmp1; $i+=2) { # 1024x1536 @tmp2 = $tmp1[$i+1] =~ m,([\w\W\s\S\d\D]*?),mg; for ($j=0; $j<@tmp2; $j++) { $xml->[0]{ValueList}{$tmp1[$i]}[$j] = $tmp2[$j]; } } } } return($xml); } ################################################################## # GET_DATEI_STRING (siehe TEMPLATE_LESEN) # Liest Datei als String ein (Schlrfmodus) # # input: absoluter Pfad zur Datei # # output: Inhalt der Datei ################################################################## sub GET_DATEI_STRING { my $Datei=$_[0]; my $DateiInhalt = ''; my $FH = "FILE".int(rand()*10000); if (-e $Datei) { open ($FH, "< $Datei") || die "Kann Datei $Datei nicht oeffen: $!\n"; local $/; # Schlrfmodus einschlaten while (<$FH>) { $DateiInhalt = $_; # gesamte Datei drin! } close ($FH) || die "Can't close the text file: $!\n"; } else {die "Datei $Datei kann nicht geoeffnet werden";} return ($DateiInhalt); } ################################################################## # UTF8_LATIN1 # Nicht schn und intelligent, dafr geht es auch ohne Modul # # input: $string utf8-codiert # # output: $string latin1-codiert # ################################################################## sub UTF8_LATIN1 { #my $string = $_[0]; # Zerlegt einen String in einzelne Zeichen #my @chars = unpack("U" x length($_[0]), $_[0]); # "U" unicode #my @chars = unpack("a1" x length($_[0]), $_[0]); #my ($char); #$_[0] =~ tr/\0-\x{ff}//UC; # utf8 to latin1 char # Fr Perl 5.8 #use Encode; #$octets = encode("iso-8859-15", $utf8string); $_[0] =~ s/“|”/"/mg; # bertragung von 3-Byte-Zeichen $_[0] =~ s/([])/$1/mg; $_[0] =~ s/Ñ//mg; $_[0] =~ s/Ò//mg; $_[0] =~ s/á//mg; $_[0] =~ s/â//mg; $_[0] =~ s/ã//mg; $_[0] =~ s/ä//mg; $_[0] =~ s/å//mg; $_[0] =~ s/æ//mg; $_[0] =~ s/ç//mg; $_[0] =~ s/è//mg; $_[0] =~ s/é//mg; $_[0] =~ s/ê//mg; $_[0] =~ s/ë//mg; $_[0] =~ s/ì//mg; $_[0] =~ s/í//mg; $_[0] =~ s/î//mg; $_[0] =~ s/ï//mg; $_[0] =~ s/ð//mg; $_[0] =~ s/ñ//mg; $_[0] =~ s/ò//mg; $_[0] =~ s/ó//mg; $_[0] =~ s/ô//mg; $_[0] =~ s/õ//mg; $_[0] =~ s/ö//mg; $_[0] =~ s/÷//mg; $_[0] =~ s/ø//mg; $_[0] =~ s/ù//mg; $_[0] =~ s/ú//mg; $_[0] =~ s/û//mg; $_[0] =~ s/ü//mg; $_[0] =~ s/ý//mg; $_[0] =~ s/þ//mg; $_[0] =~ s/ÿ//mg; $_[0] =~ s/ //mg; $_[0] =~ s/À//mg; $_[0] =~ s/Á//mg; $_[0] =~ s/Ä//mg; $_[0] =~ s/Å//mg; $_[0] =~ s/Æ//mg; $_[0] =~ s/Ç//mg; $_[0] =~ s/È//mg; $_[0] =~ s/É//mg; $_[0] =~ s/Ê//mg; $_[0] =~ s/Ë//mg; $_[0] =~ s/Ì//mg; $_[0] =~ s/Í//mg; $_[0] =~ s/Î//mg; $_[0] =~ s/Ï//mg; $_[0] =~ s/Ð//mg; $_[0] =~ s/Ó//mg; $_[0] =~ s/Ô//mg; $_[0] =~ s/Õ//mg; $_[0] =~ s/Ö//mg; $_[0] =~ s/×//mg; $_[0] =~ s/Ø//mg; $_[0] =~ s/Ù//mg; $_[0] =~ s/Ú//mg; $_[0] =~ s/Û//mg; $_[0] =~ s/Ü//mg; $_[0] =~ s/Ý//mg; $_[0] =~ s/Þ//mg; $_[0] =~ s/ß//mg; $_[0] =~ s/Ã//mg; $_[0] =~ s/Â//mg; return ($_[0]); } 1; __END__ =head1 NAME FMPro::fmp2perl - Parst FMPro-XML und gibt Feldnamen, -werte und Requestparameter als Referenz zurEck. =head1 SYNOPSIS use FMPro::fmp2perl qw(fmp2perl); $tmp = ; oder ; $values = fmp2perl($tmp); $title = $values->[0]{title}{Data}[0]; $database = $values->[0]{CurrentDatabase}; $RecID = $values->[0]{CurrentRecID}; $type = $values->[0]{title}{Type}; # only for FMPXMLRESULT =head1 DESCRIPTION fmp2perl parst FMPro-XML und gibt Feldnamen, -werte und Requestparameter als Referenz zurEck. Das XML kann als FMPDSORESULT oder FMPXMLRESULT Ebergeben werden. fmp2perl ist auf UNIX- und WINDOWS-Systemen lauffEhig. Bisher nur unter ActivePerl 5.8 getestet. =head2 Input =over 4 =item B enthElt eine XML-Struktur der Form FMPBRESULT oder FMPBRESULT. Es wird nur UTF-8 Kodierung berEcksichtigt. =item B enthElt eine XML-Struktur der Form FMPBRESULT oder FMPBRESULT. Es wird nur UTF-8 Kodierung berEcksichtigt. =item B enthElt einen FMPro-Datenbank-Request. =back =head2 Output ErlEuterungen zur Output-Tabelle: I<$v = fmp2perl($tmp)> X/U - $tmp ist URL im FMPXMLRESULT Format X/FS - $tmp ist Pfad zu File oder String ~ D/U - $tmp ist URL im FMPDSORESULT Format D/FS - $tmp ist Pfad zu File oder String ~ Wert - z.B. {}{Data}[0]......$v->[0]{}{Data}[0] mit $v ->[0]- 1. Datensatz {}- Name des Feldes {Data}- Ausgabe des Feldinhaltes [0]- 0. Wiederholung eines Wiederholfeldes bzw. Feld ohne Wiederholung B x x {CurrentFoundCount} x x x x {}{Data}[0..-1] x x {}{EmptyOk} x x {}{MaxReapeat} x x {}{Type} x x x x {CurrentFormat} x x {CurrentAction} x x x x {CurrentDatabase} x x x x {CurrentError} x x x x {CurrentLayout} x x {CurrentLOP} x x {CurrentMax} x x x x {CurrentModID} x x x x {CurrentOutCount} x x {CurrentRecordCount} x x {CurrentSkip} x x {CurrentSort} x x x x {CurrentRecID} x x x x {CurrentRecordNumber} x x {DateFormat} x x {ProductBuild} x x {ProductName} x x {ProductVersion} x x {TimeFormat} x x {URL} Die Variablennamen wurden in Anlehnung an die CDML-Syntax vergeben. Daher wird die Lektuere der CDML-Referenz zur Bedeutung der Werte empfohlen. Einzige Ausnahme ist der Wert {CurrentOutCount} ( also z.B. $v->[2]{CurrentOutCount} ). {CurrentOutCount} - Anzahl der Datensaetze in der XML-Struktur; nur bei FMPBRESULT =head1 EXAMPLE use FMPro::fmp2perl qw(fmp2perl); # Beispiel mit URL FMPDSORESULT $tmp = "http://www.domain.com/FMPro?-db=your-database.fp5"; $tmp .= "&-lay=your-layout&-Format=-dso_xml&-FindAll="; $values = fmp2perl($tmp); # Beispiel mit URL FMPXMLRESULT $tmp = "http://www.domain.com/FMPro?-db=your-database.fp5"; $tmp .= "&-lay=your-layout&-Format=-fmp_xml&-FindAll="; $values = fmp2perl($tmp); # Beipiel mit Angabe eines Files (UNIX) $tmp = "/xml_data/path/fmp_xml_file.xml"; $values = fmp2perl($tmp); # Beipiel mit Angabe eines Files (WINDOWS) $tmp = "C:\\Inetpub\\wwwroot\\xml_data_path\\fmp_xml_file.xml"; $values = fmp2perl($tmp); # Beispiel mit Uebergabe einer FMPBRESULT XML-Struktur $tmp =<<__XML__; 0 YOUR-DATABASE.FP5 your title __XML__ $values = fmp2perl($tmp); # Beispiel mit Uebergabe einer FMPBRESULT XML-Struktur $tmp =<<__XML__; 0 your title __XML__ $values = fmp2perl($tmp); $title = $values->[0]{title}{Data}[0]; $database = $values->[0]{CurrentDatabase}; $RecID = $values->[0]{CurrentRecID}; $type = $values->[0]{title}{Type}; # only for FMPXMLRESULT =head1 BUGS =head1 AUTHOR Hans-Martin Aurich info@webconsultant.de technik@baurat.de =head1 COPYRIGHT Copyright 2004 - 2005 Hans-Martin Aurich. All rights reserved. =head1 SEE ALSO =cut